> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cocobase.cc/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript / TypeScript SDK

> Complete reference for the CocoBase JavaScript and TypeScript SDK (v1.6)

# 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](https://www.npmjs.com/package/cocobase)

## Installation

<Tabs>
  <Tab title="npm">
    ```bash theme={null}
    npm install cocobase
    ```
  </Tab>

  <Tab title="yarn">
    ```bash theme={null}
    yarn add cocobase
    ```
  </Tab>

  <Tab title="pnpm">
    ```bash theme={null}
    pnpm add cocobase
    ```
  </Tab>

  <Tab title="Browser (CDN)">
    ```html theme={null}
    <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>
    ```
  </Tab>
</Tabs>

***

## Initialization

```typescript theme={null}
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:

```typescript theme={null}
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?)`

```typescript theme={null}
// 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)`

```typescript theme={null}
const doc = await db.getDocument<Post>("posts", "doc-id");
console.log(doc.data.title);
```

### `createDocument<T>(collection, data)`

```typescript theme={null}
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.

```typescript theme={null}
await db.updateDocument("posts", "doc-id", { title: "New Title" });
```

### `replaceDocument<T>(collection, id, data)`

Full replace — sends all fields, replaces the document entirely.

```typescript theme={null}
await db.replaceDocument("settings", "config-1", { theme: "dark", lang: "en" });
```

### `deleteDocument(collection, id)`

```typescript theme={null}
await db.deleteDocument("posts", "doc-id");
```

***

## Batch Operations

```typescript theme={null}
// 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 }`      |

### Sorting & pagination

```typescript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
// 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.

```typescript theme={null}
// 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:**

```typescript theme={null}
useEffect(() => {
  return db.onSnapshot<Post>("posts", setPosts);
}, []);
```

***

## Pagination Helper — `paginate`

Stateful pagination — tracks page position, detects `hasMore`, no backend changes needed.

```typescript theme={null}
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

```typescript theme={null}
// 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).

```typescript theme={null}
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`.

```typescript theme={null}
const unsub = db.auth.onAuthStateChange((user, token) => {
  if (user) setLoggedIn(true);
  else setLoggedIn(false);
});

// Cleanup
unsub();
```

### `onAuthEvent` — all auth lifecycle events

```typescript theme={null}
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

```typescript theme={null}
// 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)

```typescript theme={null}
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

```typescript theme={null}
// 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

```typescript theme={null}
const expiry = db.auth.getTokenExpiry(); // Date | null
const expired = db.auth.isTokenExpired(); // boolean
```

***

## Real-time

### Watch a collection

```typescript theme={null}
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)

```typescript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
const rooms = await db.realtime.listRooms();
```

***

## Cloud Functions

```typescript theme={null}
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:

```typescript theme={null}
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

```typescript theme={null}
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`:

```typescript theme={null}
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

* [Flutter SDK](/sdk-reference/flutter) — Dart/Flutter equivalent
* [MCP Server](/sdk-reference/mcp) — AI assistant integration
* [Authentication guide](/core-concepts/authentication)
* [Querying guide](/features/querying)
* [Real-time guide](/features/realtime)
