TypeScript SDK

Idiomatic Node.js and TypeScript client for InferaDB.

Coming soon. The TypeScript SDK is under active development. The API surface shown here is based on the Rust SDK and may change before release.

Fully typed async client for InferaDB. Works with Node.js 18+, Bun, and Deno.

Installation

npm install @inferadb/sdk

Authentication

Three authentication methods:

Method Use Case Security
Client Credentials (Ed25519 JWT) Service-to-service High
Bearer Token User sessions, OAuth Medium
API Key Testing, simple integrations Basic
import { InferaDB, Ed25519PrivateKey } from "@inferadb/sdk";

async function main() {
  const client = new InferaDB({
    url: "https://engine.inferadb.com",
    credentials: {
      clientId: "my-client",
      privateKey: await Ed25519PrivateKey.fromPemFile("client.pem"),
      certificateId: "cert-id",
    },
  });
}

Bearer Token

const client = new InferaDB({
  url: "https://engine.inferadb.com",
  token: process.env.INFERADB_TOKEN,
});

API Key

const client = new InferaDB({
  url: "https://engine.inferadb.com",
  apiKey: process.env.INFERADB_API_KEY,
});

Permission Checks

const vault = client.organization("my-org").vault("production");

const allowed = await vault.check("user:alice", "can_edit", "document:readme");

With ABAC Context

// Context keys are schema-defined
const allowed = await vault.check("user:alice", "can_view", "document:readme", {
  context: { ip_address: "10.0.0.1" },
});

Require — Throws on Deny

// Throws AccessDeniedError if permission is denied
await vault.require("user:alice", "can_edit", "document:readme");

With Consistency Token

const allowed = await vault.check("user:alice", "can_view", "document:readme", {
  atLeastAsFresh: revisionToken,
});

Batch Check

const results = await vault.checkBatch([
  { subject: "user:alice", permission: "can_edit", resource: "document:readme" },
  { subject: "user:bob", permission: "can_view", resource: "document:readme" },
]);

if (results.allAllowed()) {
  // all checks passed
}

Relationships

Write

// Returns a revision token
const token = await vault.relationships.write({
  resource: "document:readme",
  relation: "editor",
  subject: "user:alice",
});

Batch Write

await vault.relationships.writeBatch([
  { resource: "document:readme", relation: "editor", subject: "user:alice" },
  { resource: "document:readme", relation: "viewer", subject: "user:bob" },
]);

List

const rels = await vault.relationships
  .list({ resource: "document:readme" })
  .collect();

Delete

await vault.relationships.deleteWhere({
  resource: "document:readme",
  relation: "viewer",
  subject: "user:bob",
});

Lookups

// What resources can Alice view?
const resources = await vault.resources
  .accessibleBy("user:alice")
  .withPermission("can_view")
  .resourceType("document")
  .collect();

// Who can edit this document?
const subjects = await vault.subjects
  .withPermission("can_edit")
  .onResource("document:readme")
  .collect();

Testing

MockClient (Fastest)

Stub specific check results. Call assertExpectations() to verify all stubs were hit.

import { MockClient } from "@inferadb/sdk/testing";

const client = new MockClient()
  .onCheck("user:alice", "can_edit", "document:readme").allow()
  .onCheck("user:bob", "can_edit", "document:readme").deny()
  .onCheckAnySubject("can_view", "document:readme").allow()
  .defaultDeny();

client.assertExpectations();

InMemoryClient (Full Policy Evaluation)

import { InMemoryClient } from "@inferadb/sdk/testing";

const client = await InMemoryClient.withSchemaAndData(
  `
  type document {
      relation viewer
      relation editor
      relation can_view = viewer | editor
  }
  `,
  [
    { resource: "document:readme", relation: "editor", subject: "user:alice" },
    { resource: "document:readme", relation: "viewer", subject: "user:bob" },
  ],
);

TestVault (Real Instance)

Auto-cleans up on dispose. Call vault.preserve() to keep data for debugging.

import { TestVault } from "@inferadb/sdk/testing";

const vault = await TestVault.create(org, { schema: schemaIpl });

Error Handling

import { AccessDeniedError, InferaDBError } from "@inferadb/sdk";

try {
  await vault.require("user:alice", "can_edit", "document:readme");
} catch (error) {
  if (error instanceof AccessDeniedError) {
    // permission denied
  } else if (error instanceof InferaDBError) {
    if (error.isRetriable) {
      // retry after error.retryAfter milliseconds
    }
    console.error(error.kind, error.requestId);
  }
}

ErrorKind values: "unauthorized", "forbidden", "not_found", "rate_limited", "schema_violation", "unavailable", "timeout", "invalid_argument".

Framework Integrations

Express Middleware

import { inferadbMiddleware } from "@inferadb/sdk/express";

app.use(
  "/api/documents/:id",
  inferadbMiddleware(vault, {
    subject: (req) => `user:${req.user.id}`,
    resource: (req) => `document:${req.params.id}`,
    permission: (req) => (req.method === "GET" ? "can_view" : "can_edit"),
  }),
);

Next.js

import { withAuthorization } from "@inferadb/sdk/next";

export const GET = withAuthorization(vault, {
  subject: (req) => `user:${req.auth.userId}`,
  resource: (req) => `document:${req.nextUrl.searchParams.get("id")}`,
  permission: "can_view",
})(async (req) => {
  return Response.json({ data: "authorized content" });
});