Error Handling
BunBaseError
Section titled “BunBaseError”All SDK methods throw BunBaseError on non-2xx responses.
import { BunBaseError } from "@bunbase/js";
try { await client.auth.login({ email, password });} catch (err) { if (err instanceof BunBaseError) { console.log(err.message); // "Invalid email or password." console.log(err.status); // 401 console.log(err.code); // machine-readable code, e.g. "invalid_credentials" console.log(err.field); // field name for validation errors, e.g. "email" console.log(err.body); // raw server response body }}Field errors
Section titled “Field errors”When the server returns a validation error tied to a specific field, err.field is set. Use this to display inline form errors:
} catch (err) { if (err instanceof BunBaseError && err.field) { setFieldErrors({ [err.field]: err.message }); } else { setError(err instanceof Error ? err.message : "Something went wrong."); }}Common status codes
Section titled “Common status codes”| Status | Cause |
|---|---|
400 | Validation error |
401 | Not authenticated or session expired |
403 | Forbidden — insufficient permissions |
404 | Record or resource not found |
409 | Conflict — e.g. email already registered |
413 | File too large for bucket |
415 | MIME type not allowed by bucket |
429 | Rate limit exceeded |
TypeScript types
Section titled “TypeScript types”BunBaseRecord
Section titled “BunBaseRecord”System fields on every record:
interface BunBaseRecord { _id: string; // ULID — unique, time-ordered _created_at: number; // Unix ms _updated_at: number; // Unix ms _owner_id: string | null; _deleted_at?: number | null;}ListResult<T>
Section titled “ListResult<T>”interface ListResult<T> { items: T[]; total: number; page: number; limit: number; next_cursor: string | null;}AuthResult / AuthUser
Section titled “AuthResult / AuthUser”interface AuthResult { access_token: string; refresh_token: string; expires_in: number; user: AuthUser;}
interface AuthUser { id: string; email: string; created_at: number; updated_at: number; is_verified: boolean;}FileRecord
Section titled “FileRecord”interface FileRecord { id: string; key: string; bucket: string; filename: string | null; // original filename, e.g. "photo.jpg" collection: string | null; record_id: string | null; owner_id: string | null; size: number; mime_type: string; is_public: boolean; created_at: number;}Pass filename to client.storage.downloadUrl(id, filename) to produce an extension-aware URL (e.g. /api/v1/storage/01JKX.../photo.jpg). This helps browsers and CDNs identify the file type from the URL path.
LoginResult / TotpChallenge
Section titled “LoginResult / TotpChallenge”login() returns LoginResult, which is either AuthResult or a TotpChallenge:
type LoginResult = AuthResult | TotpChallenge;
interface TotpChallenge { totp_required: true; totp_token: string; // short-lived — pass to verifyTotp()}Check which branch you received:
const result = await client.auth.login({ email, password });
if ("totp_required" in result) { // 2FA step required} else { // result is AuthResult — user is signed in}ApiKey
Section titled “ApiKey”interface ApiKey { id: string; name: string; created_at: number; // Unix ms}createApiKey() returns ApiKey & { key: string } — the key field (bb_...) is only present on creation. Store it securely; it cannot be retrieved again.
RealtimeEvent<T>
Section titled “RealtimeEvent<T>”type RealtimeEventType = "create" | "update" | "delete";
interface RealtimeEvent<T = BunBaseRecord> { event: RealtimeEventType; collection: string; record: T & BunBaseRecord;}ListQuery<T>
Section titled “ListQuery<T>”interface ListQuery<T> { filter?: Filter<T>; sort?: string; // prefix "-" for descending page?: number; limit?: number; fields?: string[]; expand?: string[]; after?: string; // cursor — replaces page when set include_deleted?: boolean;}