Skip to main content

JavaScript SDK

Complete guide to using Cocobase with JavaScript and TypeScript applications.

Installation

npm:
npm install cocobase
yarn:
yarn add cocobase
pnpm:
pnpm add cocobase

Quick Start

Initialize Cocobase

import { Cocobase } from "cocobase";

const db = new Cocobase({
  apiKey: "YOUR_API_KEY",
  projectId: "YOUR_PROJECT_ID",
  baseURL: "https://api.cocobase.buzz", // Optional, defaults to this
});

Basic Operations

// List documents
const docs = await db.listDocuments("posts");

// Get single document
const doc = await db.getDocument("posts", "doc-id");

// Create document
const created = await db.createDocument("posts", {
  title: "My First Post",
  content: "Hello World!",
});

// Update document
await db.updateDocument("posts", "doc-id", {
  title: "Updated Title",
});

// Delete document
await db.deleteDocument("posts", "doc-id");

Batch Operations

// Create multiple documents
await db.createDocuments("users", [
  { name: "Alice", email: "alice@example.com" },
  { name: "Bob", email: "bob@example.com" },
]);

// Delete multiple documents
await db.deleteDocuments("users", ["id-1", "id-2", "id-3"]);

// Update multiple documents
await db.updateDocuments("users", {
  "id-1": { status: "active" },
  "id-2": { status: "inactive" },
});

Counting & Aggregation

// Count documents matching a filter
const { count } = await db.countDocuments("users", {
  filters: { status: "active" }
});

// Aggregate (sum, avg, min, max, count)
const result = await db.aggregateDocuments("orders", {
  field: "price",
  operation: "avg",
  query: { filters: { status: "completed" } }
});
console.log(result.result); // average order price

Querying Data

Simple Filters

// Find active users older than 18
const users = await db.listDocuments("users", {
  filters: {
    status: "active",
    age__gt: 18,
  },
});

Operators Reference

Comparison Operators

OperatorFilter KeyExample
Equalfield{ status: "active" }
Greater Thanfield__gt{ age__gt: 18 }
Greater or Equalfield__gte{ age__gte: 18 }
Less Thanfield__lt{ age__lt: 65 }
Less or Equalfield__lte{ age__lte: 65 }
Not Equalfield__ne{ status__ne: "deleted" }

String Operators

OperatorFilter KeyExample
Containsfield__contains{ title__contains: "javascript" }
Starts Withfield__startswith{ email__startswith: "admin" }
Ends Withfield__endswith{ domain__endswith: ".com" }

Array Operators

OperatorFilter KeyExample
In Arrayfield__in{ status__in: "active,pending" }
Not In Arrayfield__notin{ status__notin: "deleted,archived" }

Sorting and Pagination

Sorting

// Sort by creation date (newest first)
const posts = await db.listDocuments("posts", {
  sort: "createdAt",
  order: "desc",
});

Pagination

// Get page 3 (20 items per page)
const posts = await db.listDocuments("posts", {
  limit: 20,
  offset: 40,
});

Type Safety with TypeScript

Define Your Types

interface Book {
  title: string;
  author: string;
  price: number;
  publishedAt?: string;
}

// Typed list
const books = await db.listDocuments<Book>("books");

// Full type safety
books.forEach((doc) => {
  console.log(doc.data.title); // TypeScript knows this is a string
  console.log(doc.data.price); // TypeScript knows this is a number
});

// Typed single document
const book = await db.getDocument<Book>("books", "doc-id");
console.log(book.data.author);

Create Typed Documents

const newBook: Book = {
  title: "Clean Code",
  author: "Robert Martin",
  price: 45.99,
};

const created = await db.createDocument<Book>("books", newBook);
console.log(created.id);

Authentication

Email/Password

// Register
await db.auth.register({
  email: "user@example.com",
  password: "securePassword123",
  data: { username: "johndoe" } // optional extra fields
});

// Get user after registration
const user = await db.auth.getUser();
console.log("User ID:", user.id);

// Login
await db.auth.login({
  email: "user@example.com",
  password: "securePassword123",
});

// Get user after login
const loggedInUser = await db.auth.getUser();

// Logout
await db.auth.logout();

// Check current user
const currentUser = await db.auth.getCurrentUser();

OAuth

// Google OAuth (requires Google ID token)
const user = await db.auth.loginWithGoogle({
  idToken: "google-id-token",
  platform: "web" // or "android" / "ios"
});

Advanced Authentication

Auth Event Callbacks

// Listen to auth state changes
db.auth.onAuthEvent({
  onLogin: (user, token) => {
    console.log("User logged in:", user.email);
  },
  onLogout: () => {
    console.log("User logged out");
  },
  onRegister: (user, token) => {
    console.log("User registered:", user.email);
  },
  onUserUpdate: (user) => {
    console.log("User updated:", user.email);
  },
  onTokenChange: (token) => {
    console.log("Token changed:", token);
  },
  onAuthStateChange: (user, token) => {
    console.log("Auth state changed:", user?.email);
  }
});

// Remove all callbacks
db.auth.clearAuthCallbacks();

Google OAuth

const user = await db.auth.loginWithGoogle({
  idToken: "google-id-token",
  platform: "web" // or "mobile", "ios", "android"
});
console.log("Logged in:", user.email);

GitHub OAuth

// 1. Redirect user to GitHub OAuth URL
const githubAuthUrl = `https://github.com/login/oauth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}`;
window.location.href = githubAuthUrl;

// 2. Handle callback with the code
const code = new URLSearchParams(window.location.search).get("code");
const user = await db.auth.loginWithGithub({
  code: code,
  redirectUri: "http://localhost:3000/auth/github/callback",
  platform: "web"
});
console.log("Logged in:", user.email);

Role Checking

if (db.auth.hasRole("admin")) {
  console.log("User is an admin");
}

List Users

const users = await db.auth.listUsers({
  filters: { status: "active" },
  limit: 10
});
console.log("Total users:", users.total);

Get User By ID

const user = await db.auth.getUserById("user-123");
console.log("User:", user.email);

Password Reset

// Request reset email
await db.auth.requestPasswordReset("user@example.com");
console.log("Reset email sent");

Email Verification

// Request verification email
await db.auth.requestEmailVerification();

// Verify with token from email
await db.auth.verifyEmail("verification-token-from-email");

// Resend verification email
await db.auth.resendVerificationEmail();

Two-Factor Authentication (2FA)

// Enable 2FA for current user
await db.auth.enable2FA();
console.log("2FA enabled");

// Disable 2FA
await db.auth.disable2FA();
console.log("2FA disabled");

// Send 2FA code to email
await db.auth.send2FACode("user@example.com");

// Login flow when 2FA is enabled
const result = await db.auth.login({
  email: "user@example.com",
  password: "password123"
});

if (result.requires_2fa) {
  console.log("2FA required:", result.message);
  
  // After user enters their code
  const user = await db.auth.verify2FALogin({
    email: "user@example.com",
    code: "123456"
  });
  console.log("2FA login successful:", user.email);
} else {
  console.log("Logged in:", result.user?.email);
}

Real-time Data

Watch a Collection

const watcher = db.realtime.collection("posts", { status: "active" });
// watcher is a CollectionWatcher instance

Project Broadcast

const broadcast = db.realtime.broadcast(userId, userName);

Room Chat

const room = db.realtime.room("room-id", userId, userName);

Multiplayer Game

Initialize Game Client

const game = db.realtime.game("my-game-function");

Connect to a Room

// Basic connection
await game.connect({ roomId: "game-room-1" });

// With authentication
await game.connect({
  roomId: "private-game",
  token: userJwtToken
});

// With metadata (sent to server on connect)
await game.connect({
  roomId: "game-1",
  metadata: { playerName: "John", team: "red" }
});

Listen for Events

// Listen for connection
game.on("connected", (data) => {
  console.log("Connected! My player ID:", data.your_id);
});

// Listen for players joining/leaving
game.on("player_joined", (data) => {
  console.log("Player joined:", data.player_id);
});

game.on("player_left", (data) => {
  console.log("Player left:", data.player_id);
});

// Listen for game state updates
game.on("state", (data) => {
  renderPlayers(data.players);
});

// Listen for tick events
game.on("tick", (data) => {
  updateGameLoop(data);
});

// Listen for errors
game.on("error", (data) => {
  console.error("Game error:", data);
});

// Listen for disconnection
game.on("disconnected", (data) => {
  console.log("Disconnected:", data.reason);
});

// Listen to ALL events (wildcard)
game.on("*", ({ event, data }) => {
  console.log("Event:", event, data);
});

One-time Event Listener

// Called once then automatically removed
game.once("connected", (data) => {
  console.log("Connected with ID:", data.your_id);
});

Remove Event Listeners

// Remove specific listener
const unsub = game.on("player_joined", handler);
unsub(); // unsubscribe

// Remove specific listener by reference
game.off("player_joined", handler);

// Remove all listeners for an event
game.removeAllListeners("player_joined");

// Remove ALL listeners
game.removeAllListeners();

Send Actions to Server

// Send a move action
game.send({ action: "move", x: 100, y: 200 });

// Send a chat message
game.send({ action: "chat", message: "Hello!" });

// Send any custom action
game.send({ action: "shoot", angle: 45, power: 80 });

Check Connection Status

// Check if connected
if (game.isConnected) {
  console.log("Connected");
}

// Get current player ID
console.log("My ID:", game.playerId);

Disconnect

// Disconnect and disable auto-reconnect
game.disconnect();

// Disconnect but keep auto-reconnect enabled
game.disconnect(false);

Auto-Reconnect

// Auto-reconnect is enabled by default with exponential backoff
// Disable it
game.setAutoReconnect(false);

// Re-enable it
game.setAutoReconnect(true);

List Game Rooms

const { rooms, total } = await db.realtime.listGameRooms();
rooms.forEach(room => {
  console.log(`${room.room_id}: ${room.player_count}/${room.max_players} players`);
});

// List all rooms including private
const { rooms } = await db.realtime.listGameRooms(false);

List Rooms

const rooms = await db.realtime.listRooms();

File Storage

// Create a document with files
await db.createDocumentWithFiles(
  "posts",
  { title: "Post with Image", content: "Hello!" },
  {
    image: file,             // single file
    gallery: [img1, img2]    // multiple files
  }
);

// Update a document with files
await db.updateDocumentWithFiles(
  "users",
  "user-123",
  { bio: "Updated bio" },   // optional data update
  { avatar: newAvatarFile } // optional file update
);

Error Handling

try {
  const doc = await db.getDocument("posts", "non-existent-id");
} catch (error) {
  if (error.code === "NOT_FOUND") {
    console.log("Document not found");
  } else if (error.code === "UNAUTHORIZED") {
    console.log("Not authorized");
  } else {
    console.log("Error:", error.message);
  }
}

Best Practices

1. Always Use Types

// Good
const books = await db.listDocuments<Book>("books");

// Avoid
const books = await db.listDocuments("books");

2. Always Set Limits

// Good
const posts = await db.listDocuments("posts", {
  limit: 20,
});

// Bad - could return thousands
const posts = await db.listDocuments("posts");

3. Handle Errors

try {
  const doc = await db.getDocument("posts", id);
  return doc;
} catch (error) {
  console.error("Failed to fetch:", error);
  return null;
}

4. Use Environment Variables

const db = new Cocobase({
  apiKey: process.env.COCOBASE_API_KEY,
});

Next Steps