Skip to main content

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 install cocobase

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

OptionTypeDefaultDescription
apiKeystringYour project API key (required)
projectIdstring""Project ID (required for cloud functions)
timeoutnumber30000Per-request timeout in ms
storageStorageAdapterauto-detectToken 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

SuffixMeaningExample
(none)Equal{ status: "active" }
__gtGreater than{ age__gt: 18 }
__gteGreater or equal{ age__gte: 18 }
__ltLess than{ price__lt: 100 }
__lteLess or equal{ price__lte: 100 }
__neNot equal{ status__ne: "deleted" }
__containsSubstring match{ title__contains: "flutter" }
__startswithStarts with{ email__startswith: "admin" }
__endswithEnds with{ domain__endswith: ".com" }
__inIn list (comma-sep){ status__in: "active,pending" }
__notinNot in list{ role__notin: "bot,spam" }
__isnullIs null / not null{ deletedAt__isnull: true }

Sorting & pagination

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);
}, []);

Pagination Helper — paginate

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:
PropertyTypeDescription
statusCodenumberHTTP status code
urlstringRequest URL
methodstringHTTP method
detailunknownRaw server error body
suggestionstringHuman-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