@rennitlb/ttlive-client
v0.1.0
Published
Typed client for TT-Live (tischtennislive.de) table tennis data
Maintainers
Readme
@rennitlb/ttlive-client
Typed client for TT-Live table tennis data.
TT-Live exposes its data through a set of .NET XML endpoints (app.web4sport.de) with German field names and inconsistent shapes. @rennitlb/ttlive-client fetches, parses and normalizes them into plain, typed JavaScript objects.
Unofficial. This package is not affiliated with TT-Live or web4sport.
Installation
npm install @rennitlb/ttlive-clientRequires Node.js 20 or later. The package is ESM-only.
Usage
import { createTtliveClient } from '@rennitlb/ttlive-client'
const client = createTtliveClient({ associationId: 397 }) // BeTTV
const groups = await client.getGroups()
const league = await client.getLeague(groups[0].leagueIds[0])
console.log(league?.name, league?.teams.length)Fetch everything at once
getSnapshot() fetches all groups of the association, every league in them, and derives unique clubs and players:
const { groups, associations, leagues, clubs, players } =
await client.getSnapshot()leaguesinclude agroupNameand contain theirteamsandfixtures.clubsare derived from team names (e.g.TTC Neukölln II→TTC Neukölln) and de-duplicated by short name.playerswho play in several teams or halves are merged into one player with all theirscores.
Leagues are fetched one after another to go easy on the upstream server, so a full snapshot can take a few minutes.
API
createTtliveClient(options)
| Option | Type | Default | Description |
| ------------------- | --------------------- | ---------------------------- | ------------------------------------------------------------------------------------- |
| associationId | number | – | Association (Verband) ID, e.g. 397 for BeTTV. |
| includeSecondHalf | boolean \| 'auto' | 'auto' | Also fetch second half (Rückrunde) data. 'auto' includes it from January to August. |
| baseUrl | string | 'https://app.web4sport.de' | Endpoint base URL, e.g. to route requests through a proxy. |
| fetch | typeof fetch | globalThis.fetch | Custom fetch implementation (caching, retries, testing). |
| logger | { warn(msg): void } | silent | Receives non-fatal warnings, e.g. leagues without data. |
Returns a client with:
| Method | Returns | Description |
| --------------------- | ------------------------- | ---------------------------------------------------------------------------- |
| getAssociations() | Promise<Association[]> | All associations known to TT-Live. |
| getGroups() | Promise<Group[]> | Groups (Damen, Erwachsene, Jugend, …) with league IDs. |
| getLeague(leagueId) | Promise<League \| null> | League with teams, players and fixtures, or null. |
| getSnapshot() | Promise<Snapshot> | Everything above, see Fetch everything at once. |
Data model
All types are exported from the package entry.
| Type | Key fields |
| ------------- | --------------------------------------------------------------------------------------------------------------- |
| Association | id, name, sportCategoryId, logo |
| Group | name, leagueIds |
| League | id, associationId, name, shortName, teams, fixtures |
| Team | id, leagueId, name, clubName, standings (position, won, …), playersFirstHalf, playersSecondHalf |
| Player | id, name, scores |
| PlayerScore | playerId, teamId, position, isSecondHalf, won, lost, pk1Diff–pk4Diff, score (LivePZ) |
| Fixture | leagueId, nr, date, homeTeamId, guestTeamId, result (e.g. [8, 2]), isFirstHalf, link |
| Club | name, shortName |
Errors
Network failures, non-OK HTTP responses and unparsable XML throw a TtliveError:
import { TtliveError } from '@rennitlb/ttlive-client'
try {
await client.getGroups()
} catch (error) {
if (error instanceof TtliveError) {
console.error(error.url, error.status, error.cause)
}
}An unknown league ID is not an error: getLeague() returns null.
Runtime support
The client only uses the global fetch and has no Node.js-specific imports, so it runs on Node.js, Bun, Deno and edge runtimes. Use it on the server (Next.js server components or route handlers, Astro, Remix loaders, build scripts, …). Browsers are likely blocked by CORS; use baseUrl to point at your own proxy if you need client-side access.
CommonJS
The package is ESM-only. From CommonJS, either load it with await import('@rennitlb/ttlive-client') or bundle it into your output (e.g. tsup/esbuild noExternal).
Development
npm install
npm test # vitest, against recorded XML responses in test/fixtures
npm run typecheck
npm run lint
npm run buildLicense
MIT
