Skip to content

Using the SDK

@gymgym/sdk is the only way apps talk to the backend. It bundles three things. (Not using TypeScript? Call the same API with raw GraphQL and a personal API key.)

Operations. Each named operation in packages/sdk/operations becomes a method with typed variables and result. query Me becomes gym.me(), mutation SaveWorkout becomes gym.saveWorkout(variables):

const gym = createGymClient() // in the browser: same origin, cookies
const { viewer } = await gym.me()
const { bodyweight } = await gym.bodyweight()

Auth. gym.auth is a Better Auth client with the anonymous, passkey, admin and device authorization plugins already configured:

await gym.auth.signIn.social({ provider: 'github', callbackURL: '/app' })
await gym.auth.signIn.passkey()

Offline queue. Saves made while offline are queued in gym.queue and replayed in order when the connection returns. Pass storage to persist the queue; the web app passes localStorage and calls gym.queue.flush() when the browser comes back online.

Option Use
baseUrl API origin. Omit in the browser to use the current origin.
token Bearer token, or a function returning one, for clients without cookies (CLI, native apps).
fetch Custom fetch for tests, React Native or service bindings.
onMutation Called after every successful mutation, for cache invalidation.

Failures throw GymError with a code: UNAUTHENTICATED, FORBIDDEN, NOT_FOUND, BAD_USER_INPUT, NETWORK or HTTP_<status>. Someone else’s id and a missing id both give NOT_FOUND.

Write the query or mutation in packages/sdk/operations/<area>.graphql, run pnpm contract, and the new method appears on the client. If the server field doesn’t exist yet, add it in packages/api first; the isolation test picks the new operation up automatically.