api-sdk

SKILL.md

TypeScript SDK

The Bknd TypeScript SDK provides a type-safe, promise-based client for interacting with Bknd's REST API. Use it for both client-side browser applications and server-side code.

What You'll Learn

  • Initialize the Api class with different auth strategies
  • Perform CRUD operations on entities
  • Handle authentication and token management
  • Access media and system APIs
  • Use type safety with auto-generated DB types

Quick Start

import { Api } from "bknd";

const api = new Api({ host: "https://api.example.com" });

// Read data
const posts = await api.data.readMany("posts", { limit: 10 });

// Login with password
await api.auth.login("password", { email: "user@example.com", password: "pass" });

Api Class

The Api class is the main entry point for all SDK operations.

Constructor Options

const api = new Api({
  host: string,           // API base URL (default: http://localhost)
  headers?: Headers,      // Custom headers
  storage?: Storage,      // For token persistence (see below)
  key?: string,           // Token storage key (default: "auth")
  token?: string,         // Direct JWT token
  user?: SafeUser | null, // User object (server-side)
  request?: Request,      // Extract from Hono/Next.js request
  credentials?: "include" | "same-origin" | "omit",
  onAuthStateChange?: (state: AuthState) => void,
  fetcher?: ApiFetcher,   // Custom fetch implementation
  verbose?: boolean,
  // Module-specific options
  data?: Partial<DataApiOptions>,
  auth?: Partial<AuthApiOptions>,
  media?: Partial<MediaApiOptions>,
});

Storage Interface

For client-side token persistence, provide a storage object:

const api = new Api({
  host: "https://api.example.com",
  storage: {
    getItem: (key) => localStorage.getItem(key),
    setItem: (key, value) => localStorage.setItem(key, value),
    removeItem: (key) => localStorage.removeItem(key),
  },
  onAuthStateChange: (state) => {
    console.log("Auth state:", state);
  },
});

Initialization Patterns

Client-side with localStorage:

const api = new Api({
  host: "https://api.example.com",
  storage: {
    getItem: (key) => localStorage.getItem(key),
    setItem: (key, value) => localStorage.setItem(key, value),
    removeItem: (key) => localStorage.removeItem(key),
  },
});

Server-side with Request (Next.js/Hono):

const api = new Api({
  request: req, // Automatically extracts token from cookies/headers
});

Direct Token:

const api = new Api({
  host: "https://api.example.com",
  token: "your-jwt-token",
});

User Object (server-side, no token):

const api = new Api({
  host: "https://api.example.com",
  user: { id: 1, email: "user@example.com" },
  verified: true,
});

Data API

Accessed via api.data. Provides CRUD operations on entities.

Read Operations

// Read single entity
const post = await api.data.readOne("posts", 1);

// Read many with query
const posts = await api.data.readMany("posts", {
  limit: 10,
  offset: 0,
  sort: "-created_at",
  where: { published: true },
  with: ["author", "comments"],
});

// Read one by filter
const post = await api.data.readOneBy("posts", {
  where: { slug: "hello-world" },
});

// Read related entities
const comments = await api.data.readManyByReference("posts", 1, "comments", {
  limit: 20,
});

Create Operations

// Create single
const newPost = await api.data.createOne("posts", {
  title: "Hello",
  content: "World",
  author_id: 1,
});

// Create many
const newPosts = await api.data.createMany("posts", [
  { title: "First", author_id: 1 },
  { title: "Second", author_id: 2 },
]);

Update Operations

// Update single
const updated = await api.data.updateOne("posts", 1, {
  title: "Updated title",
});

// Update many
await api.data.updateMany("posts", { published: false }, {
  published: true,
});

Delete Operations

// Delete single
await api.data.deleteOne("posts", 1);

// Delete many
await api.data.deleteMany("posts", { archived: true });

Utility Methods

// Count records
const { count } = await api.data.count("posts", { published: true });

// Check existence
const { exists } = await api.data.exists("posts", { slug: "hello-world" });

Auth API

Accessed via api.auth. Handles authentication and session management.

// Login with strategy
const res = await api.auth.login("password", {
  email: "user@example.com",
  password: "password",
});

// Register
const res = await api.auth.register("password", {
  email: "user@example.com",
  password: "password",
});

// Get current user
const { user } = await api.auth.me();

// Logout
await api.auth.logout(); // Clears token from storage

// Get available strategies
const { strategies } = await api.auth.strategies();

// Custom action (OAuth, etc.)
const actionRes = await api.auth.action("google", "callback", {
  code: "...",
  state: "...",
});

Media API

Accessed via api.media. Handles file uploads and management.

// Get upload info for direct upload
const { url, headers } = await api.media.getUploadInfo("uploads/image.jpg");

// Upload directly (returns URL)
const { url } = await api.media.upload(file);

// Delete file
await api.media.deleteFile("uploads/image.jpg");

System API

Accessed via api.system. System-level operations.

// Health check
await api.system.health();

// Get schema
const schema = await api.system.schema();

Auth State Management

Track authentication state throughout your application.

// Get current auth state
const state = api.getAuthState();
// { token?: string, user?: SafeUser, verified: boolean }

// Check if authenticated
if (api.isAuthenticated()) {
  const user = api.getUser();
}

// Verify token with server
const verifiedState = await api.getVerifiedAuthState();

// Manually update token
api.updateToken("new-jwt-token");

Auth State Callback

const api = new Api({
  host: "https://api.example.com",
  onAuthStateChange: (state) => {
    if (state.user) {
      console.log("Logged in as:", state.user.email);
    } else {
      console.log("Logged out");
    }
  },
});

Type Safety

Import auto-generated types from "bknd" for full type safety.

import type { DB, RepoQueryIn, SafeUser } from "bknd";
import { Api } from "bknd";

const api = new Api({ host: "https://api.example.com" });

// Entity types are auto-generated
type Post = DB["posts"];

// Query types are inferred
const query: RepoQueryIn = {
  limit: 10,
  where: { published: true },
};

// API methods return typed results
const posts = await api.data.readMany("posts", query);
// posts is RepositoryResultJSON<Selectable<DB["posts"]>[]>

Error Handling

All API methods return a response object. Check ok before accessing data.

const res = await api.data.readOne("posts", 1);

if (!res.ok) {
  console.error("Error:", res.body);
  return;
}

console.log("Post:", res.body);

Module-Specific Options

Configure individual modules with custom options.

const api = new Api({
  host: "https://api.example.com",
  data: {
    queryLengthLimit: 2000,
    defaultQuery: { limit: 20 },
  },
  auth: {
    basepath: "/api/custom-auth",
    credentials: "include",
  },
});

Common Patterns

Fetch with Auto-Auth

const api = new Api({
  host: "https://api.example.com",
  storage: localStorage,
});

// Login first
await api.auth.login("password", { email: "user@example.com", password: "pass" });

// Subsequent requests automatically include the token
const posts = await api.data.readMany("posts");

Server-Side Protected Request

// In Next.js server component or Hono middleware
const api = new Api({ request: req });

if (!api.isAuthenticated()) {
  return new Response("Unauthorized", { status: 401 });
}

const user = api.getUser();
const posts = await api.data.readMany("posts", { author_id: user.id });

Custom Fetcher

const api = new Api({
  host: "https://api.example.com",
  fetcher: async (input, init) => {
    console.log("Request:", input, init);
    const response = await fetch(input, init);
    console.log("Response:", response.status);
    return response;
  },
});

DOs and DON'Ts

DO:

  • Use api.data.readMany() for most read operations with query parameters
  • Provide storage option for client-side token persistence
  • Use api.getAuthState() to check authentication status
  • Import types from "bknd" for type safety

DON'T:

  • Forget to handle errors by checking res.ok
  • Use readOneBy for single entity lookups by ID (use readOne instead)
  • Manually set headers for authentication (SDK handles this)
  • Mix token and user object in same Api instance

See Also

Weekly Installs
1
GitHub Stars
2
First Seen
Jan 19, 2026
Installed on
opencode1
claude-code1