SDK Reference
Official SDKs for ZendBX with full TypeScript support. Choose your language to get started.
@zendbx/sdk
TypeScript SDK with full generic support for React, Next.js, Vue, Svelte, Node.js, and modern JS runtimes.
zendbx
Async Python SDK for FastAPI, Flask, Django, and any Python 3.8+ application.
TypeScript SDK
The official TypeScript SDK for ZendBX with full type safety and generic support. Works in React, Next.js, Vue, Svelte, Node.js, and any modern JS runtime.
Installation
npm install @zendbx/sdkClient Initialization
Creates and returns a ZendBX client instance. Call this once and export the result.
import { createClient } from '@zendbx/sdk';
const client = createClient({
apiUrl: 'https://api.zendbx.in',
projectSlug: 'my-project',
anonKey: 'your-anon-key',
});const client = createClient({
apiUrl: process.env.ZENDBX_URL!,
projectSlug: 'my-project',
anonKey: process.env.ZENDBX_ANON_KEY!,
accessToken: 'user-jwt-token', // Optional: for authenticated requests
autoRefreshToken: true, // Optional: auto-refresh expired tokens
});| Parameter | Type | Required | Description |
|---|---|---|---|
| apiUrl | string | required | ZendBX API URL (e.g., https://api.zendbx.in) |
| projectSlug | string | required | Your project slug identifier |
| anonKey | string | required | Project anonymous (public) key for client-side |
| accessToken | string | optional | Optional JWT token for authenticated requests |
| autoRefreshToken | boolean | optional | Auto-refresh expired tokens (default: false) |
Authentication
Complete authentication system with email/password, OAuth, and session management.
Sign Up
const { data, error } = await client.auth.signUp({
email: 'user@example.com',
password: 'secure-password-123',
name: 'John Doe', // optional
});
if (error) {
console.error('Sign up failed:', error.message);
} else {
console.log('User created:', data.user);
console.log('Access token:', data.access_token);
}
// Response Type:
// interface AuthResponse {
// access_token: string;
// user: User;
// }Sign In
const { data, error } = await client.auth.signIn({
email: 'user@example.com',
password: 'secure-password-123',
});
// Token is automatically stored in the client
console.log('Logged in:', data.user.email);Get Current User
const user = await client.auth.getUser();
console.log('Current user:', user.email);Get Session Token
const token = client.auth.getSession();
console.log('Current token:', token);Set Session Manually
// Useful for SSR or when restoring a session
client.auth.setSession('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');Sign Out
await client.auth.signOut();
// Token is cleared from the clientRefresh Session
const { data } = await client.auth.refreshSession();
console.log('New token:', data.access_token);Password Reset
// Request password reset
await client.auth.resetPasswordForEmail('user@example.com');
// User receives email with reset token
// Update password with token
await client.auth.updatePassword('reset-token-from-email', 'new-password');Email Verification
await client.auth.verifyEmail('verification-token-from-email');Update User
const user = await client.auth.updateUser({
name: 'Jane Doe',
email: 'jane@example.com'
});Database Operations
All database operations use client.from(tableName) which returns a chainable query builder.
SELECT
// Select all columns
const { data, error } = await client.from('users').select('*');
// Select specific columns
const { data } = await client.from('users').select('id, name, email');
// With TypeScript types
interface User {
id: string;
name: string;
email: string;
created_at: string;
}
const { data } = await client.from<User>('users').select('*');
// data is User[] | null with full autocomplete// Get total count with results
const { data, count } = await client.from('users')
.select('*', { count: 'exact' });
console.log(`Found ${count} users`);
// Count Types:
// 'exact' - Precise count (slower on large tables)
// 'planned' - Estimate from query planner
// 'estimated' - Fast estimate from statisticsFiltering
All filters are chainable and map to PostgREST query parameters.
// Equality
.eq('status', 'active')
.neq('role', 'admin')
// Comparison
.gt('age', 18)
.gte('score', 90)
.lt('price', 100)
.lte('stock', 10)
// String matching
.like('email', '%@gmail.com')
.ilike('name', '%john%') // case-insensitive
// Array/NULL checks
.in('status', ['active', 'pending'])
.is('deleted_at', null)
// Negation
.not('status', 'eq', 'deleted')
// OR conditions (PostgREST syntax)
.or('status.eq.active,status.eq.pending')
// Chaining filters (AND logic)
const { data } = await client.from('users')
.select('*')
.eq('country', 'India')
.gt('age', 18)
.like('email', '%@gmail.com')
.is('verified', true);INSERT
// Single row insert
const { data, error } = await client.from('users').insert({
name: 'John Doe',
email: 'john@example.com',
status: 'active'
});
// Insert returns empty array by default
// Chain .select() to return the inserted row
const { data } = await client.from('users')
.insert({ name: 'John' })
.select();
console.log('Inserted user:', data[0]);// Multiple rows (homogeneous)
const { data, error } = await client.from('users').insert([
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
{ name: 'Carol', email: 'carol@example.com' }
]).select();
console.log(`Inserted ${data.length} users`);
// Heterogeneous bulk insert (different columns per row)
const { data } = await client.from('products').insert([
{ name: 'Widget', price: 19.99, sku: 'WDG-001' },
{ name: 'Gadget', price: 29.99 }, // no SKU
{ name: 'Doohickey' } // no price or SKU
]).select();
// IMPORTANT: Bulk inserts are atomic - all rows insert or none doUPDATE
// Update with filter
const { data, error } = await client.from('users')
.update({ status: 'inactive' })
.eq('id', '123');
// Update multiple rows
await client.from('users')
.update({ verified: true })
.gt('created_at', '2024-01-01');
// Update and return updated rows
const { data } = await client.from('users')
.update({ email: 'newemail@example.com' })
.eq('id', '123')
.select();DELETE
// Delete with filter
await client.from('users').delete().eq('id', '123');
// Delete multiple rows
await client.from('logs')
.delete()
.lt('created_at', '2024-01-01');
// Delete with multiple conditions
await client.from('users')
.delete()
.eq('status', 'inactive')
.lt('last_login', '2023-01-01');UPSERT
// Insert or update if exists (based on unique constraints)
const { data, error } = await client.from('users')
.upsert({
email: 'john@example.com',
name: 'John Updated',
status: 'active'
})
.select();
// Upsert with onConflict (specify which column to check)
const { data } = await client.from('products')
.upsert({
sku: 'WIDGET-001',
name: 'Widget Pro',
price: 29.99
}, {
onConflict: 'sku'
})
.select();.eq(), etc.) before .update() or .delete() to avoid modifying all rows.Ordering
// Single order clause
const { data } = await client.from('products')
.select('*')
.order('price', { ascending: false });
// Multiple order clauses
const { data } = await client.from('products')
.select('*')
.order('category')
.order('price', { ascending: false });Pagination
// Limit
const { data } = await client.from('users')
.select('*')
.limit(20);
// Range (offset + limit)
// Get rows 0-19 (page 1)
const { data } = await client.from('users')
.select('*')
.range(0, 19);
// Get rows 20-39 (page 2)
const { data } = await client.from('users')
.select('*')
.range(20, 39);
// Pagination example
const pageSize = 20;
const page = 2; // 0-indexed
const { data, count } = await client.from('users')
.select('*', { count: 'exact' })
.range(page * pageSize, (page + 1) * pageSize - 1);
console.log(`Page ${page + 1} of ${Math.ceil(count / pageSize)}`);Single Row Operations
// .single() - Returns a single object instead of an array
// Throws error if 0 or multiple rows found
const { data, error } = await client.from('users')
.select('*')
.eq('id', '123')
.single();
// data is User | null (not User[] | null)
// .maybeSingle() - Like single() but returns null instead of error when no rows found
const { data } = await client.from('users')
.select('*')
.eq('email', 'john@example.com')
.maybeSingle();
// data is User | null, error is null even if no rows foundStorage
File upload, download, and management backed by Backblaze B2.
List Buckets
const { data: buckets } = await client.storage.listBuckets();Create Bucket
const { data } = await client.storage.createBucket('avatars', {
public: true
});Upload File
const file = document.getElementById('file-input').files[0];
const { data, error } = await client.storage
.from('avatars')
.upload('user-123/profile.jpg', file, {
contentType: 'image/jpeg',
cacheControl: '3600',
upsert: true // Overwrite if exists
});Download File
const { data: blob } = await client.storage
.from('avatars')
.download('user-123/profile.jpg');
// Create download URL
const url = URL.createObjectURL(blob);Get Public URL
const { data } = client.storage
.from('avatars')
.getPublicUrl('user-123/profile.jpg');
console.log(data.publicUrl);
// https://api.zendbx.in/p/my-project/v1/storage/buckets/avatars/files/user-123/profile.jpgCreate Signed URL (Private Files)
const { data } = await client.storage
.from('documents')
.createSignedUrl('contract.pdf', 3600); // 1 hour
console.log(data.signedUrl);Delete Files
await client.storage
.from('avatars')
.remove(['user-123/old-avatar.jpg']);List Files in Bucket
const { data: files } = await client.storage
.from('avatars')
.list('user-123/');TypeScript Support
Full type safety with generic row types and exported interfaces.
Exported Types
import type {
// Response types
ZendbxResponse,
ZendbxError,
// Auth types
User,
Session,
AuthData,
SignUpCredentials,
SignInCredentials,
// Query types
FilterOperator,
OrderClause,
SelectOptions,
// Storage types
StorageBucket,
StorageObject,
StorageUploadResult,
// Database types
DatabaseRow,
JsonValue,
// Error classes
ZendbxSDKError,
AuthExpiredError,
} from '@zendbx/sdk';Custom Row Types
// Extend DatabaseRow for type safety
interface Product extends DatabaseRow {
id: string;
name: string;
price: number;
category: 'electronics' | 'clothing' | 'food';
in_stock: boolean;
}
const { data } = await client.from<Product>('products')
.select('*')
.eq('category', 'electronics')
.gt('price', 100);
// Full IntelliSense support
if (data) {
data.forEach(product => {
console.log(product.name, product.price);
});
}Error Handling
Every operation returns { data, error }. Never throws for database errors. Check error before using data.
Check Error Field
const { data, error } = await client.from('users').select('*');
if (error) {
console.error('Query failed:', error.message);
console.error('Status:', error.status);
console.error('Details:', error.details);
return;
}
// Safe to use data here
console.log('Users:', data);Error Object Structure
interface ZendbxError {
message: string; // Human-readable error message
status?: number; // HTTP status code
details?: unknown; // Additional context
code?: string; // Error code for programmatic handling
hint?: string; // Suggestion for fixing the error
}SDK Error Classes
import {
ZendbxSDKError,
MissingConfigError,
InvalidUrlError,
AuthExpiredError,
ProjectNotFoundError,
StorageProviderError
} from '@zendbx/sdk';
try {
const client = createClient({
apiUrl: '', // Invalid
projectSlug: 'test',
anonKey: 'key'
});
} catch (error) {
if (error instanceof MissingConfigError) {
console.error('Configuration error:', error.message);
}
}Best Practices
Use Environment Variables
const client = createClient({
apiUrl: process.env.ZENDBX_URL!,
projectSlug: process.env.ZENDBX_PROJECT_SLUG!,
anonKey: process.env.ZENDBX_ANON_KEY!
});Lazy Query Building
// Build query conditionally
let query = client.from('products').select('*');
if (category) query = query.eq('category', category);
if (minPrice) query = query.gte('price', minPrice);
if (inStock) query = query.eq('in_stock', true);
// Execute once
const { data } = await query;Use .select() for Returning Data
// Good - returns inserted data
const { data } = await client.from('users')
.insert({ name: 'John' })
.select();
// Returns empty array without .select()
const { data } = await client.from('users')
.insert({ name: 'John' });
// data: []Complete Example
import { createClient } from '@zendbx/sdk';
import type { User } from '@zendbx/sdk';
// Initialize client
const client = createClient({
apiUrl: process.env.ZENDBX_URL!,
projectSlug: process.env.ZENDBX_PROJECT_SLUG!,
anonKey: process.env.ZENDBX_ANON_KEY!
});
// Type-safe user interface
interface AppUser extends User {
subscription_tier: 'free' | 'pro' | 'enterprise';
last_login: string;
}
async function main() {
// Sign in
const { data: authData, error: authError } = await client.auth.signIn({
email: 'user@example.com',
password: 'secure-password'
});
if (authError) {
console.error('Login failed:', authError.message);
return;
}
console.log('Logged in as:', authData.user.email);
// Fetch data with filters
const { data: users, error, count } = await client
.from<AppUser>('users')
.select('*', { count: 'exact' })
.eq('subscription_tier', 'pro')
.gte('last_login', '2024-01-01')
.order('last_login', { ascending: false })
.range(0, 19);
if (error) {
console.error('Query failed:', error.message);
return;
}
console.log(`Found ${count} pro users, showing first 20:`);
users?.forEach(user => {
console.log(`- ${user.email} (${user.subscription_tier})`);
});
// Insert new record
const { data: newUser, error: insertError } = await client
.from<AppUser>('users')
.insert({
email: 'newuser@example.com',
name: 'New User',
subscription_tier: 'free'
})
.select();
if (!insertError) {
console.log('Created user:', newUser[0].id);
}
// Bulk insert
const bulkData = [
{ name: 'Alice', email: 'alice@example.com', subscription_tier: 'pro' },
{ name: 'Bob', email: 'bob@example.com', subscription_tier: 'free' },
{ name: 'Carol', email: 'carol@example.com', subscription_tier: 'enterprise' }
];
const { data: bulkUsers } = await client
.from<AppUser>('users')
.insert(bulkData)
.select();
// Upload file
const file = new File(['Hello'], 'test.txt', { type: 'text/plain' });
await client.storage
.from('documents')
.upload(`user-${authData.user.id}/hello.txt`, file);
// Sign out
await client.auth.signOut();
}
main().catch(console.error);Common Errors
| Parameter | Type | Required | Description |
|---|---|---|---|
| 401 Unauthorized | Auth Error | optional | Authentication token expired or invalid. Call client.auth.signIn() or refreshSession(). |
| 403 Forbidden | Permission | optional | Permission denied. Check RLS policies or use service_role key for admin operations. |
| 404 Not Found | Resource | optional | Resource not found. Verify table name, project slug, and that the resource exists. |
| 409 Conflict | Constraint | optional | Unique constraint violation. Use .upsert() instead or handle duplicate keys. |
| 422 Validation | Input | optional | Invalid input data. Check required fields, data types, and constraints. |

