| name | api-sdk |
| description | Use when working with the TypeScript SDK for making API calls to Bknd, handling authentication, and managing data operations from client or server code. Covers Api class initialization, CRUD operations, auth methods, and module-specific APIs. |
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" });
const posts = await api.data.readMany("posts", { limit: 10 });
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,
headers?: Headers,
storage?: Storage,
key?: string,
token?: string,
user?: SafeUser | null,
request?: Request,
credentials?: "include" | "same-origin" | "omit",
onAuthStateChange?: (state: AuthState) => void,
fetcher?: ApiFetcher,
verbose?: boolean,
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,
});
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
const post = await api.data.readOne("posts", 1);
const posts = await api.data.readMany("posts", {
limit: 10,
offset: 0,
sort: "-created_at",
where: { published: true },
with: ["author", "comments"],
});
const post = await api.data.readOneBy("posts", {
where: { slug: "hello-world" },
});
const comments = await api.data.readManyByReference("posts", 1, "comments", {
limit: 20,
});
Create Operations
const newPost = await api.data.createOne("posts", {
title: "Hello",
content: "World",
author_id: 1,
});
const newPosts = await api.data.createMany("posts", [
{ title: "First", author_id: 1 },
{ title: "Second", author_id: 2 },
]);
Update Operations
const updated = await api.data.updateOne("posts", 1, {
title: "Updated title",
});
await api.data.updateMany("posts", { published: false }, {
published: true,
});
Delete Operations
await api.data.deleteOne("posts", 1);
await api.data.deleteMany("posts", { archived: true });
Utility Methods
const { count } = await api.data.count("posts", { published: true });
const { exists } = await api.data.exists("posts", { slug: "hello-world" });
Auth API
Accessed via api.auth. Handles authentication and session management.
const res = await api.auth.login("password", {
email: "user@example.com",
password: "password",
});
const res = await api.auth.register("password", {
email: "user@example.com",
password: "password",
});
const { user } = await api.auth.me();
await api.auth.logout();
const { strategies } = await api.auth.strategies();
const actionRes = await api.auth.action("google", "callback", {
code: "...",
state: "...",
});
Media API
Accessed via api.media. Handles file uploads and management.
const { url, headers } = await api.media.getUploadInfo("uploads/image.jpg");
const { url } = await api.media.upload(file);
await api.media.deleteFile("uploads/image.jpg");
System API
Accessed via api.system. System-level operations.
await api.system.health();
const schema = await api.system.schema();
Auth State Management
Track authentication state throughout your application.
const state = api.getAuthState();
if (api.isAuthenticated()) {
const user = api.getUser();
}
const verifiedState = await api.getVerifiedAuthState();
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" });
type Post = DB["posts"];
const query: RepoQueryIn = {
limit: 10,
where: { published: true },
};
const posts = await api.data.readMany("posts", query);
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,
});
await api.auth.login("password", { email: "user@example.com", password: "pass" });
const posts = await api.data.readMany("posts");
Server-Side Protected Request
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