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, cookiesconst { 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.
Options
Section titled “Options”| 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. |
Errors
Section titled “Errors”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.
Adding an operation
Section titled “Adding an operation”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.