Documentation Index
Fetch the complete documentation index at: https://docs.cocobase.buzz/llms.txt
Use this file to discover all available pages before exploring further.
JavaScript / TypeScript SDK
The official CocoBase JS SDK — works in browsers, Node.js, Next.js, SvelteKit, Nuxt, React Native, and any JS runtime.
Package: cocobase · Version: 1.6.x · npm: npmjs.com/package/cocobase
Installation
npm
yarn
pnpm
Browser (CDN)
<script src="https://cdn.jsdelivr.net/npm/cocobase/dist/browser/cocobase.min.js"></script>
<script>
const { Cocobase } = window.CocobaseSDK;
const db = new Cocobase({ apiKey: "YOUR_API_KEY" });
</script>
Initialization
import { Cocobase } from "cocobase";
const db = new Cocobase({
apiKey: "YOUR_API_KEY",
projectId: "YOUR_PROJECT_ID", // required for cloud functions
timeout: 30000, // request timeout in ms (default: 30000)
});
SSR / Server-side (Next.js, Node.js)
By default the SDK auto-detects the environment and uses in-memory storage when localStorage is not available. For explicit control:
import { Cocobase, memoryStorageAdapter, cookieStorageAdapter } from "cocobase";
// Node.js / SSR — in-memory, no persistence
const db = new Cocobase({
apiKey: process.env.COCOBASE_API_KEY!,
storage: memoryStorageAdapter(),
});
// Browser — keep auth token in a cookie instead of localStorage
const db = new Cocobase({
apiKey: "YOUR_API_KEY",
storage: cookieStorageAdapter({ secure: true, sameSite: "lax" }),
});
CocobaseConfig options
| Option | Type | Default | Description |
|---|
apiKey | string | — | Your project API key (required) |
projectId | string | "" | Project ID (required for cloud functions) |
timeout | number | 30000 | Per-request timeout in ms |
storage | StorageAdapter | auto-detect | Token persistence adapter |
Documents — CRUD
listDocuments<T>(collection, query?)
// All documents
const posts = await db.listDocuments("posts");
// With filters, sorting, pagination
const posts = await db.listDocuments<Post>("posts", {
filters: { status: "published", age__gt: 18 },
sort: "createdAt",
order: "desc",
limit: 20,
offset: 0,
});
// doc.data is typed as Post
posts.forEach(doc => console.log(doc.data.title, doc.id, doc.createdAt));
getDocument<T>(collection, id)
const doc = await db.getDocument<Post>("posts", "doc-id");
console.log(doc.data.title);
createDocument<T>(collection, data)
const created = await db.createDocument<Post>("posts", {
title: "Hello World",
published: true,
});
console.log(created.id); // server-assigned ID
updateDocument<T>(collection, id, data)
Partial update — only fields you pass are changed. Other fields are preserved.
await db.updateDocument("posts", "doc-id", { title: "New Title" });
replaceDocument<T>(collection, id, data)
Full replace — sends all fields, replaces the document entirely.
await db.replaceDocument("settings", "config-1", { theme: "dark", lang: "en" });
deleteDocument(collection, id)
await db.deleteDocument("posts", "doc-id");
Batch Operations
// Create multiple
await db.createDocuments("users", [
{ name: "Alice", email: "alice@example.com" },
{ name: "Bob", email: "bob@example.com" },
]);
// Delete multiple
await db.deleteDocuments("users", ["id-1", "id-2", "id-3"]);
// Update multiple (id → fields map)
await db.updateDocuments("users", {
"id-1": { status: "active" },
"id-2": { status: "inactive" },
});
Querying
Filter operators
| Suffix | Meaning | Example |
|---|
| (none) | Equal | { status: "active" } |
__gt | Greater than | { age__gt: 18 } |
__gte | Greater or equal | { age__gte: 18 } |
__lt | Less than | { price__lt: 100 } |
__lte | Less or equal | { price__lte: 100 } |
__ne | Not equal | { status__ne: "deleted" } |
__contains | Substring match | { title__contains: "flutter" } |
__startswith | Starts with | { email__startswith: "admin" } |
__endswith | Ends with | { domain__endswith: ".com" } |
__in | In list (comma-sep) | { status__in: "active,pending" } |
__notin | Not in list | { role__notin: "bot,spam" } |
__isnull | Is null / not null | { deletedAt__isnull: true } |
const posts = await db.listDocuments<Post>("posts", {
sort: "createdAt",
order: "desc", // "asc" | "desc"
limit: 20,
offset: 40, // skip first 40 (page 3 of 20)
});
Count & aggregate
const { count } = await db.countDocuments("users", {
filters: { status: "active" },
});
const result = await db.aggregateDocuments("orders", {
field: "price",
operation: "avg", // "sum" | "avg" | "min" | "max" | "count"
query: { filters: { status: "completed" } },
});
console.log(result.result); // average order price
File + document combined
// Create a document AND upload files in one call
await db.createDocumentWithFiles(
"posts",
{ title: "Post with Image" },
{ image: file, gallery: [img1, img2] }
);
// Update document AND files
await db.updateDocumentWithFiles(
"users",
"user-123",
{ bio: "Updated bio" },
{ avatar: newAvatarFile }
);
Live Queries — onSnapshot
Subscribe to a collection and get called immediately with current data, then again whenever anything changes (create/update/delete). Returns an unsubscribe function to call on cleanup.
// Simple — fires immediately, then on every change
const unsub = db.onSnapshot<Post>("posts", (posts) => {
renderPosts(posts); // posts is Document<Post>[]
});
// With query
const unsub = db.onSnapshot<Post>(
"posts",
{ filters: { published: true }, sort: "createdAt", order: "desc" },
(posts) => renderPosts(posts)
);
// Cleanup (e.g. component unmount)
unsub();
React example:
useEffect(() => {
return db.onSnapshot<Post>("posts", setPosts);
}, []);
Stateful pagination — tracks page position, detects hasMore, no backend changes needed.
const pager = db.paginate<Post>("posts", { sort: "createdAt", order: "desc" }, 20);
// Fetch pages
const page1 = await pager.nextPage();
// page1.data → Document<Post>[]
// page1.hasMore → boolean
// page1.page → current page index
// page1.pageSize → 20
const page2 = await pager.nextPage();
await pager.prevPage();
await pager.goToPage(3); // zero-indexed
pager.reset(); // start over from page 0
Authentication
Register / Login / Logout
// Register
const result = await db.auth.register({
email: "user@example.com",
password: "securePassword123",
data: { username: "johndoe" }, // optional custom fields
});
console.log(result.user.id, result.token);
// Login
const result = await db.auth.login({
email: "user@example.com",
password: "securePassword123",
});
// Logout
await db.auth.logout();
// Get current user (from server)
const user = await db.auth.getCurrentUser();
// Cached user (no network call)
console.log(db.auth.user?.email);
console.log(db.auth.token);
// Is logged in?
console.log(db.auth.isAuthenticated());
Restore session on app load — initAuth
Call once on startup to restore a saved session. Safe on servers — network errors and 5xx responses do not log the user out (only explicit 401/403 from the server do).
const { user, token, isAuthenticated } = await db.auth.initAuth();
if (isAuthenticated) {
console.log("Welcome back,", user?.email);
} else {
showLoginScreen();
}
// SSR — pass token from request header/cookie
const { isAuthenticated } = await db.auth.initAuth(req.cookies["cocobase-token"]);
onAuthStateChange — reactive auth
Returns an unsubscribe function. Works like Firebase’s onAuthStateChanged.
const unsub = db.auth.onAuthStateChange((user, token) => {
if (user) setLoggedIn(true);
else setLoggedIn(false);
});
// Cleanup
unsub();
onAuthEvent — all auth lifecycle events
db.auth.onAuthEvent({
onLogin: (user, token) => console.log("Logged in:", user.email),
onRegister: (user, token) => console.log("Registered:", user.email),
onLogout: () => redirectToLogin(),
onUserUpdate: (user) => updateUserStore(user),
onAuthStateChange: (user, token) => {
// fired on initAuth() completion
if (user) restoreUserState(user);
},
onAuthRestored: (user, isAuthenticated) => {
// fired when initAuth() finishes — remove loading spinners here
setLoading(false);
},
});
OAuth
// Google (requires Google ID token)
const user = await db.auth.loginWithGoogle({
idToken: "google-id-token",
platform: "web", // "web" | "android" | "ios"
});
// GitHub
const user = await db.auth.loginWithGithub({
code: new URLSearchParams(window.location.search).get("code")!,
redirectUri: "https://myapp.com/auth/github/callback",
platform: "web",
});
Two-Factor Authentication (2FA)
const result = await db.auth.login({ email, password });
if (result.requires_2fa) {
// Prompt user for code then:
const user = await db.auth.verify2FALogin({
email: "user@example.com",
code: "123456",
});
}
Other auth methods
// Password reset
await db.auth.requestPasswordReset("user@example.com");
// Email verification
await db.auth.requestEmailVerification();
await db.auth.verifyEmail("token-from-email");
await db.auth.resendVerificationEmail();
// 2FA management
await db.auth.enable2FA();
await db.auth.disable2FA();
await db.auth.send2FACode("user@example.com");
// List users (admin)
const { users, total } = await db.auth.listUsers({ limit: 20 });
// Get user by ID
const user = await db.auth.getUserById("user-123");
// Update profile
const updated = await db.auth.updateUser({
data: { bio: "Hello!" },
email: "new@example.com",
});
// Role check
if (db.auth.hasRole("admin")) { /* ... */ }
Token helpers
const expiry = db.auth.getTokenExpiry(); // Date | null
const expired = db.auth.isTokenExpired(); // boolean
Real-time
Watch a collection
import { CollectionWatcher } from "cocobase";
const watcher: CollectionWatcher = db.realtime.collection("posts", { status: "active" });
watcher.connect();
watcher.onCreate(({ document }) => console.log("New:", document));
watcher.onUpdate(({ document }) => console.log("Updated:", document));
watcher.onDelete(({ document }) => console.log("Deleted:", document));
// Called whenever WebSocket reconnects after a drop
watcher.onReconnect(() => console.log("Reconnected, refreshing..."));
// Stop watching
watcher.disconnect();
Project broadcast (pub/sub)
import { ProjectBroadcast } from "cocobase";
const broadcast: ProjectBroadcast = db.realtime.broadcast(userId, userName);
broadcast.connect();
broadcast.onMessage((data) => console.log("Message:", data));
broadcast.send({ type: "announcement", text: "Hello everyone!" });
broadcast.disconnect();
Room chat
import { RoomChat } from "cocobase";
const room: RoomChat = db.realtime.room("room-id", userId, userName);
room.connect();
room.onMessage((msg) => appendMessage(msg));
room.send({ text: "Hey!" });
room.disconnect();
List rooms
const rooms = await db.realtime.listRooms();
Cloud Functions
const result = await db.functions.execute<{ orderId: string }>("processOrder", {
payload: { items: [{ id: "p1", qty: 2 }] },
method: "post",
});
console.log(result.result.orderId);
console.log(result.executionTime); // ms
console.log(result.output); // console output from function
// Shorthand helpers
await db.functions.get<Stats>("getStats");
await db.functions.post<Receipt>("sendEmail", { payload: { to: "a@b.com" } });
Typed Errors
All API errors throw a typed subclass of CocobaseError. Use instanceof to branch:
import {
NotFoundError,
UnauthorizedError,
ForbiddenError,
RateLimitError,
ValidationError,
ServerError,
TimeoutError,
CocobaseError,
} from "cocobase";
try {
await db.getDocument("posts", "bad-id");
} catch (e) {
if (e instanceof NotFoundError) {
showNotFound(); // 404
} else if (e instanceof UnauthorizedError) {
redirectToLogin(); // 401
} else if (e instanceof ForbiddenError) {
showAccessDenied(); // 403
} else if (e instanceof RateLimitError) {
showRetryMessage(); // 429
} else if (e instanceof ValidationError) {
showFormErrors(e.detail); // 422
} else if (e instanceof ServerError) {
showServerError(); // 5xx
} else if (e instanceof TimeoutError) {
showTimeoutMessage();
} else {
throw e;
}
}
All error classes expose:
| Property | Type | Description |
|---|
statusCode | number | HTTP status code |
url | string | Request URL |
method | string | HTTP method |
detail | unknown | Raw server error body |
suggestion | string | Human-readable fix hint |
Storage Adapters
import {
localStorageAdapter, // browser localStorage (default in browser)
memoryStorageAdapter, // in-memory, no persistence (default in Node)
autoStorageAdapter, // auto-detects browser vs Node
cookieStorageAdapter, // browser cookie — SSR-readable
} from "cocobase";
// Cookie adapter options
const storage = cookieStorageAdapter({
cookieName: "cocobase-token", // default
secure: true, // HTTPS only
sameSite: "lax",
maxAge: 60 * 60 * 24 * 7, // 7 days
});
TypeScript Types
Key types exported from cocobase:
import type {
Document, // { id, data, createdAt, updatedAt }
AppUser, // { id, email, data, roles, ... }
Page, // { data, hasMore, page, pageSize, offset }
CocobaseConfig,
Query,
StorageAdapter,
WatcherEvent,
} from "cocobase";
Next Steps