@openexercise/open-workout-client
v0.4.0
Published
A thin TypeScript client for any Open Workout server, generated from the Open Workout API core, with Lifterclub's full API as a subpath.
Maintainers
Readme
@openexercise/open-workout-client
A thin TypeScript client for any Open Workout server. It's an
openapi-fetch client typed from the Open Workout API core
(api/core.yaml), which sends your token and an Idempotency-Key on every POST, plus a few
helpers. Workouts are Open Workout Schema documents, and exercises are
Open Exercise IDs.
npm install @openexercise/open-workout-clientTwo entry points:
@openexercise/open-workout-client: the core, for any server.createOpenWorkoutClienttakes the server's address.@openexercise/open-workout-client/lifterclub: Lifterclub, the reference implementation.createLifterclubClientis typed from Lifterclub's full API (the core plus sentence parsing, records and progress, the profile, routines, programs, imports and partners), and defaults tohttps://api.lifterclub.com.
Published from openexercise/open-workout, where a tag clients-ts-v<version> releases it with npm provenance.
Use
import { changes, createOpenWorkoutClient, signatureHeader, unwrap, verifySignature } from '@openexercise/open-workout-client'
const api = createOpenWorkoutClient({ baseUrl: 'https://api.lifterclub.com', token: () => tokens.access })
// Every route in the core, typed from the document.
const logged = unwrap(
await api.POST('/v1/workouts', {
body: { external_id: 'my-42', exercises: [{ name: 'bench press', sets: [{ reps: 5, load: 185, unit: 'lb' }] }] },
}),
)
console.log(logged.summary)
// Mirror a log: every change since the last time, oldest first, deleted ones as tombstones.
for await (const change of changes(api, { updatedSince: lastSeen, includeDeleted: true })) {
if (change.kind === 'workout') store(change.workout)
else remove(change.tombstone.id)
}
// A webhook delivery, signed in OWS-Signature.
const ok = await verifySignature(subscription.secret, request.headers.get(signatureHeader), rawBody)For Lifterclub's routes beyond the core:
import { createLifterclubClient, unwrap } from '@openexercise/open-workout-client/lifterclub'
const lc = createLifterclubClient({ token: () => tokens.access })
const draft = unwrap(await lc.POST('/v1/parse', { body: { text: 'bench 3x8 at 185' } }))api.GET, POST, PATCH, PUT and DELETE return { data, error, response }. unwrap
returns data, or throws an ApiError. Its code is the error code (rate_limited,
not_found, invalid_token…), with status, detail, the full problem, and retryAfter in
seconds when the status is 429.
A POST retried with the same Idempotency-Key returns the first result instead of writing
twice. The client makes a key per call; pass your own in headers to retry across restarts.
Lifterclub also signs webhooks as Lifterclub-Signature (lifterclubSignatureHeader) until
OWS v1, for receivers written before.
Moving from @openexercise/lifterclub-client
That package was this one under its earlier name, up to 0.3.0. Install this one instead, and
import from @openexercise/open-workout-client/lifterclub to keep createLifterclubClient and
every type as they were. Read OWS-Signature (signatureHeader) on new webhook receivers.
Develop
npm run genregeneratessrc/core.d.tsandsrc/lifterclub-api.d.tsfrom the OpenAPI documents: the Lifterclub repository'sdocs/openapi/ows-core.yamlandv1.yaml, orapi/core.yamlandapi/v1.yamlnext to this package.CORE_SPEC=pathandSPEC=pathname other copies.npm run checkfails when the committed types are stale.npm test,npm run typecheck,npm run build.
