quizbase
Skip to content
Command Palette
Search for a command to run...
QuizBase · Docs

TypeScript SDK#

Install#

npm install @quizbase/client
# or pnpm add @quizbase/client
# or bun add @quizbase/client

Requires Node ≥20 (or any modern browser with fetch + AbortController). Zero runtime dependencies.

What’s inside#

  • Typed client — every endpoint typed from /openapi.json, regenerated on each release
  • Async iterator paginationlistAll() and pages() auto-follow _links.next and preserve filters across pages
  • Built-in retryRetry-After-aware, exponential backoff with jitter, retries 429 / 5xx / network errors
  • onRequest telemetry hook — wire up to PostHog / Sentry / Datadog with one callback
  • Performance-aware timeouts — defaults tuned against /docs/performance, per-endpoint overridable
  • RFC 9457 typed errorsinstanceof QuizbaseError with .requestId, .retryAfter, .problem
  • Works everywhere — Node ≥20, Deno, Bun, Cloudflare Workers, browsers (with qb_pk_* only — qb_sk_* is blocked by CORS)

Quick start#

import { createClient } from '@quizbase/client';

const client = createClient({ apiKey: process.env.QUIZBASE_API_KEY! });

const random = await client.questions.random({
  amount: 5,
  lang: 'pl',
  difficulty: 'hard', // 'trivial' | 'easy' | 'medium' | 'hard' | 'expert' (LLM-calibrated)
  quality: 'high'
});

console.log(random.data); // Question[]

Pagination#

questions, topics, tags, and subcategories are cursor-paginated. The SDK gives you two async iterators that auto-follow _links.next and preserve filters across pages — no manual cursor bookkeeping.

// Iterate every matching question across all pages.
for await (const q of client.questions.listAll({ lang: 'pl', tags_any: ['einstein'] })) {
  await db.upsert(q);
}

// Iterate page-by-page — useful for checkpoints or per-batch logging.
for await (const page of client.topics.pages({ lang: 'en' })) {
  console.log(page.meta.requestId, page.data.length);
}

Need manual control? list() still exposes _links.next and accepts a cursor query param.

Errors#

import { QuizbaseError } from '@quizbase/client';

try {
  await client.questions.random({ category: 'unknown' });
} catch (err) {
  if (err instanceof QuizbaseError) {
    console.error(err.status);          // 400
    console.error(err.problem.code);    // "invalid_query_param"
    console.error(err.problem.detail);  // human-readable message
    console.error(err.requestId);       // for support requests
    if (err.isRateLimited) console.error(err.retryAfter); // seconds
  }
}

Telemetry hook#

const client = createClient({
  apiKey: process.env.QUIZBASE_API_KEY!,
  onRequest: ({ method, endpoint, duration, status, requestId, retryCount, final }) => {
    posthog.capture('quizbase_api_call', {
      method, endpoint, duration, status, requestId, retryCount, final
    });
  }
});

Async-safe, no-op default. Fires on every attempt (including retries). final: false means another retry will follow. Errors thrown inside the hook are swallowed so telemetry can never break the caller.

Performance-aware timeouts#

Per-endpoint defaults match the public performance baseline:

EndpointDefault timeout
questions.list, topics.list, tags.list, subcategories.list, report.create15 s
questions.random, questions.get, categories.list, languages.list, topics.get, stats.get, me.get, usage.get10 s
Global default30 s

Override per-endpoint:

createClient({
  apiKey,
  timeout: 30_000,
  timeouts: {
    'questions.random': 5_000   // narrow filters → fail fast
  }
});

Source & releases#

See also#