@axtrabase/sdk
v0.1.1
Published
TypeScript/JavaScript client for the AxtraBase API — a developer-friendly REST API in front of Microsoft Dataverse.
Readme
@axtrabase/sdk
TypeScript/JavaScript client for the AxtraBase API — a developer-friendly REST API in front of Microsoft Dataverse. You don't need to know Dataverse OAuth, OData, or security roles to use this.
Install
pnpm add @axtrabase/sdkQuick start
import { AxtraBase } from "@axtrabase/sdk";
const axtra = new AxtraBase({
apiKey: process.env.AXTRABASE_API_KEY!
});
const accounts = await axtra.table("accounts").list({ limit: 10 });
console.log(accounts.data);Use this server-side (Next.js Server Components/Actions, Route Handlers,
backend services) — a live API key is a secret and should never ship to a
browser bundle (never NEXT_PUBLIC_...).
Tables
const accounts = await axtra.table("accounts");
await accounts.list({ limit: 25, select: ["name", "revenue"] });
await accounts.get(id);
await accounts.create({ name: "Acme" });
await accounts.update(id, { name: "Acme Inc" });
await accounts.delete(id);Pagination
const page = await axtra.table("accounts").list({ limit: 50 });
page.data;
page.nextCursor;
const next = await axtra.table("accounts").list({ cursor: page.nextCursor });Or, for simple cases:
for await (const account of axtra.table("accounts").iterate()) {
console.log(account);
}Events (Change Feed)
Canonical Dataverse change events — created/updated/deleted, across every table this API key can read.
// Paged, like tables:
const page = await axtra.events.list({ limit: 50 });
page.data; // AxtraBaseEvent[]
page.nextCursor;
// Or walk every page:
for await (const event of axtra.events.iterate({ table: "account" })) {
console.log(event.type, event.table, event.recordId);
}
// One event by its public id:
await axtra.events.get("evt_3f9c2e1a-4b7d-4a2e-9c6f-1a2b3c4d5e6f");Live tail (Server-Sent Events)
for await (const event of axtra.events.stream({ table: "account" })) {
console.log(event); // arrives in real time, no polling
}With no cursor, stream() starts from "now" — it never replays retained
history. Pass after (a cursor from list()'s nextCursor or a previously
streamed event.id) to catch up first, then continue live. stream()
reconnects automatically on a transient disconnect (never on a 401/403 or
other terminal error) and resumes from the last event you actually received —
delivery is at-least-once: a reconnect can redeliver an event you already
saw, so dedupe by event.id if that matters to your consumer. Pass signal
(an AbortSignal) to stop the stream, or just break out of the loop.
const controller = new AbortController();
const stream = axtra.events.stream({ signal: controller.signal });
// later: controller.abort();Errors
import { AxtraBase, AxtraBaseError } from "@axtrabase/sdk";
try {
await axtra.table("accounts").list();
} catch (error) {
if (error instanceof AxtraBaseError) {
console.log(error.code); // e.g. "AXTRA_TABLE_PERMISSION_DENIED"
console.log(error.status); // e.g. 403
console.log(error.requestId); // paste into AxtraBase → Requests to see exactly what happened
}
}Configuration
new AxtraBase({
apiKey: "axb_live_...",
baseUrl: "http://localhost:3002" // defaults to the production AxtraBase API
});Runtime support
Native fetch, ESM, no dependencies. Works in Node 20+, Next.js server-side,
Bun, and any modern JS runtime with global fetch.
