@tuzzle/client
v0.1.2
Published
Typed API client for the Tuzzle DAM platform
Readme
@tuzzle/client
Fully typed SDK for the Tuzzle DAM platform. Types are generated from the API's OpenAPI 3.1 spec. The runtime is an ergonomic resource/upload/pagination layer plus hardening (retries, timeout, typed errors) over openapi-fetch — the low-level GET/POST/... surface is still there underneath.
bun add @tuzzle/clientUsage
import { createTuzzleClient } from '@tuzzle/client'
const client = createTuzzleClient({
baseUrl: 'https://api.tzzl.io',
accessToken: '...',
refreshToken: '...',
onTokensRefreshed: ({ access_token, refresh_token }) => save({ access_token, refresh_token }),
})
// Low-level: every path, param, body, and response is typed from the live spec.
const { data, error } = await client.GET('/api/v1/spaces')Resource namespaces
Ergonomic, fully typed accessors that delegate to the verbs above and return the
same { data, error, response } result:
const { data } = await client.files.list({ params: { query: { space_id } } })
await client.files.get(id)
await client.folders.create({ body: { space_id, name: 'images' } })
await client.folders.update(id, { name: 'renamed' })
await client.spaces.delete({ body: { id } }) // id goes in the body, not the path
await client.webhooks.create(space, { url, events: ['file.uploaded'] })
await client.members.list(space)
await client.apiKeys.revoke(space, key)
await client.sessions.revoke(id)
await client.analytics.summary(space, { period: '30d' })Resources: files, folders, spaces, collections, uploadConfigs,
webhooks, members, invitations, apiKeys, sessions, analytics.
Upload
client.upload(file, opts) does a high-level multipart upload to
POST /api/v1/upload. It uses XMLHttpRequest (for onProgress) when available
and falls back to fetch (Node), injecting the same auth header as the client.
const result = await client.upload(file, {
space: 'my-space-handle', // the space HANDLE, not its id
uploadConfig: 'avatars', // optional
onProgress: pct => console.log(`${pct}%`),
signal: controller.signal, // optional
})
result.files // File[]Pagination
Every cursor-paginated list ({ data, next_cursor }) gets paginate (async
iterable of items) and listAll (collect all items). A generic paginate /
collect helper is exported for custom pages. Pagination stops on a null or
repeated cursor (loop guard) and on an empty page.
for await (const file of client.files.paginate({ space_id }))
console.log(file.id)
const all = await client.files.listAll({ space_id })Auth modes & refresh
- OAuth tokens — injects
Authorization: Bearer <accessToken>. On a401, if a refresh token is set, callsPOST /api/v1/auth/refresh, stores the new tokens, firesonTokensRefreshed, and retries the original request once. The refresh call itself is never retried (no loops). - API key — pass
{ apiKey }instead. It is injected asAuthorization: Bearer <apiKey>and401s are not refreshed. client.setTokens({ access_token, refresh_token })andclient.getAccessToken()manage OAuth tokens manually.
Hardening
Layered, in order, inside the fetch wrapper: timeout -> retry -> auth/refresh.
const client = createTuzzleClient({
baseUrl,
timeoutMs: 10_000, // AbortController, composed with any per-call signal
retry: { retries: 2, baseDelayMs: 200, maxDelayMs: 10_000 },
throwOnError: true, // verb/resource methods throw TuzzleApiError instead of returning { error }
})- Retries: on
429,5xx, and network errors, with exponential backoff + full jitter, honoringRetry-After(seconds or HTTP-date). Idempotent methods (GET/PUT/DELETE/HEAD/OPTIONS) retry on all retryable statuses; non-idempotent methods (POST/PATCH) retry only on 429 by default (setretry.retryNonIdempotentto opt in for 5xx/network). Passretry.sleep(orbaseDelayMs: 0) for zero-delay tests. - Typed errors:
TuzzleApiError(withstatus,statusText, parsedbody,errors,url) and theisTuzzleApiError(e)guard. The low-level{ data, error }return is unchanged; opt into throwing viathrowOnError.
Regenerating types
Types live in src/schema.d.ts, generated from openapi.json:
bun run gen # openapi-typescript ./openapi.json -o ./src/schema.d.tsRe-snapshot openapi.json from a running API via its /openapi.json endpoint.
