https://github.com/public-transport/cached-hafas-client
Pass in a HAFAS client, cache data from it.
https://github.com/public-transport/cached-hafas-client
cache hafas hafas-client public-transport redis sqlite transit
Last synced: 11 months ago
JSON representation
Pass in a HAFAS client, cache data from it.
- Host: GitHub
- URL: https://github.com/public-transport/cached-hafas-client
- Owner: public-transport
- License: isc
- Created: 2018-08-27T17:57:33.000Z (almost 8 years ago)
- Default Branch: main
- Last Pushed: 2025-01-02T22:50:02.000Z (over 1 year ago)
- Last Synced: 2025-08-01T19:37:19.651Z (12 months ago)
- Topics: cache, hafas, hafas-client, public-transport, redis, sqlite, transit
- Language: JavaScript
- Homepage:
- Size: 240 KB
- Stars: 10
- Watchers: 3
- Forks: 1
- Open Issues: 7
-
Metadata Files:
- Readme: readme.md
- License: license.md
Awesome Lists containing this project
README
# cached-hafas-client
**Pass in a [HAFAS client](https://github.com/public-transport/hafas-client/tree/6), cache data from it.**
[](https://www.npmjs.com/package/cached-hafas-client)

[](https://github.com/sponsors/derhuerst)
[](https://twitter.com/derhuerst)
*Note:* This package is mainly **intended to prevent expensive and/or frequent API calls to HAFAS**. As a side effect, it *may* reduce local CPU load & latency, but that depends on the specific use case.
`cached-hafas-client`'s core logic is separated from data storage code; You can pick the store implementation that fits your use case best. Right now the following stores are implemented:
store name | built on top of | notes
-----------|-----------------|------
[`cached-hafas-client/stores/redis.js`](stores/redis.js) | [Redis](https://redis.io/) |
[`cached-hafas-client/stores/sqlite.js`](stores/sqlite.js) | [SQLite](https://www.sqlite.org/) | TTL not implemented yet
[`cached-hafas-client/stores/in-memory.js`](stores/in-memory.js) | in-memory (using [`quick-lru`](https://npmjs.com/package/quick-lru)) |
## Installation
```shell
npm install cached-hafas-client
```
## Usage
Let's set up a cached `hafas-client` instance.
```js
// create HAFAS client
import {createVbbHafas} from 'vbb-hafas'
const hafas = createVbbHafas('my-awesome-program')
// create a store backed by Redis
import Redis from 'ioredis'
import {createRedisStore} from 'cached-hafas-client/stores/redis.js'
const redis = new Redis()
const store = createRedisStore(redis)
// wrap HAFAS client with cache
import {createCachedHafasClient as withCache} from 'cached-hafas-client'
const cachedHafas = withCache(hafas, store)
```
Because `cached-hafas-client` caches HAFAS responses by "request signature", it is build on the assumption that, HAFAS works deterministically, aside from the ever-changing transit data underneath. Because there are no guarantees for this, use `cached-hafas-client` with a grain of salt.
This is why **you must send deterministic queries**; for example, you *must* pass `opt.duration` to [`departures()`](https://github.com/public-transport/hafas-client/blob/6/docs/departures.md)/[`arrivals()`](https://github.com/public-transport/hafas-client/blob/6/docs/arrivals.md), so that `cached-hafas-client` knows the time frame that the list of results returned by HAFAS is for.
```js
const wollinerStr = '900000007105'
const husemannstr = '900000110511'
const when = new Date(Date.now() + 60 * 60 * 1000)
// will fetch fresh data from HAFAS
await cachedHafas.departures(wollinerStr, {duration: 10, when})
// within the time frame of the departures() call above,
// so it will use the cached data
await cachedHafas.departures(wollinerStr, {
duration: 3, when: new Date(+when + 3 * 60 * 1000)
})
```
*Note:* `cached-hafas-client` is only compatible with [`hafas-client@5`](https://github.com/public-transport/hafas-client/tree/6).
### with a custom cache TTL
By default, `cached-hafas-client` uses TTLs that try to strike a balance between up-to-date-ness and a cache hit ratio: The caching duration depends on how far in the future you query for.
You can pass custom cache TTLs per `hafas-client` method, either as static values or as a function returning the cache TTL based on the arguments.
```js
const SECOND = 1000
const MINUTE = 60 * SECOND
const cachePeriods = {
// cache all cachedHafas.stop(…) calls for 10m
stop: 10 * MINUTE,
// cache cachedHafas.trip(tripId, opt) based on sqrt(opt.when - now)
trip: (_, opt = {}) => {
const diffSecs = (new Date(opt.when) - Date.now()) / SECOND
if (Number.isNaN(diffSecs)) return 10 * SECOND // fallback
return Math.round(Math.pow(diffSecs, 1/2) * SECOND)
},
}
const cachedHafas = withCache(hafas, store, {cachePeriods})
```
### Counting cache hits & misses
```js
cachedHafas.on('hit', (hafasClientMethod, ...args) => {
console.info('cache hit!', hafasClientMethod, ...args)
})
cachedHafas.on('miss', (hafasClientMethod, ...args) => {
console.info('cache miss!', hafasClientMethod, ...args)
})
```
### Bypassing the cache
```js
import {CACHED} from 'cached-hafas-client'
// will always fresh data
await cachedHafas.departures(wollinerStr, {[CACHED]: false})
```
### tracking hits & misses as Prometheus metrics
You can optionally track the number of hits & misses as two [Prometheus Counters](https://prometheus.io/docs/concepts/metric_types/#counter) `cached_hafas_client_hits_total` & `cached_hafas_client_misses_total`, respectively:
```js
import {trackCachingMetrics} from 'cached-hafas-client/with-metrics.js'
trackCachingMetrics(cachedHafas) // will keep metrics now
```
## API
```js
createCachedHafas(hafas, storage, opt = {})
```
`hafas` must be a [`hafas-client@6`](https://github.com/public-transport/hafas-client/tree/6)-compatible API client.
`opt` overrides this default configuration:
```js
{
cachePeriods: {
departures: 30_1000, arrivals: 30_1000, // 30s
journeys: 30_1000, // 30s
refreshJourney: 60_1000, // 1m
trip: 30_1000, // 30s
radar: 10_1000, // 10s
locations: 3_600_1000, // 1h
stop: 3_600_1000, // 1h
nearby: 3_600_1000, // 1h
reachableFrom: 30_1000,
},
}
```
## Contributing
If you have a question or need support using `cached-hafas-client`, please double-check your code and setup first. If you think you have found a bug or want to propose a feature, use [the issues page](https://github.com/public-transport/cached-hafas-client/issues).