Skip to main content

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.

Chat App Example

Build a complete real-time chat application with rooms, direct messages, and typing indicators.

Features

  • Public and private chat rooms
  • Direct messages
  • Real-time message delivery
  • Typing indicators
  • Online presence
  • Message history

Data Models

interface Room {
  name: string;
  description?: string;
  type: "public" | "private" | "direct";
  members: string[];
  createdBy: string;
  createdAt: string;
}

interface Message {
  roomId: string;
  senderId: string;
  senderName: string;
  content: string;
  type: "text" | "image" | "file";
  attachmentUrl?: string;
  createdAt: string;
  readBy: string[];
}

interface Presence {
  userId: string;
  status: "online" | "away" | "offline";
  lastSeen: string;
}

Implementation

import { Cocobase } from "cocobase";

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

// Rooms
async function createRoom(name: string, type: "public" | "private") {
  const user = await db.auth.getUser();

  return await db.createDocument("rooms", {
    name,
    type,
    members: [user.id],
    createdBy: user.id,
    createdAt: new Date().toISOString(),
  });
}

async function createDirectMessage(otherUserId: string) {
  const user = await db.auth.getUser();
  const members = [user.id, otherUserId].sort();

  // Check if DM already exists
  const existing = await db.listDocuments("rooms", {
    filters: {
      type: "direct",
      members__contains: members.join(","),
    },
  });

  if (existing.length > 0) {
    return existing[0];
  }

  return await db.createDocument("rooms", {
    name: "Direct Message",
    type: "direct",
    members,
    createdBy: user.id,
    createdAt: new Date().toISOString(),
  });
}

async function getRooms() {
  const user = await db.auth.getUser();

  return await db.listDocuments("rooms", {
    filters: {
      members__contains: user.id,
      orderBy: "createdAt",
      order: "desc",
    },
  });
}

async function joinRoom(roomId: string) {
  const user = await db.auth.getUser();
  const room = await db.getDocument("rooms", roomId);

  if (room.data.members.includes(user.id)) {
    return room;
  }

  return await db.updateDocument("rooms", roomId, {
    members: [...room.data.members, user.id],
  });
}

// Messages
async function sendMessage(
  roomId: string,
  content: string,
  type: "text" | "image" | "file" = "text",
  attachmentUrl?: string
) {
  const user = await db.auth.getUser();
  const profile = await db.getDocument("users", user.id);

  return await db.createDocument("messages", {
    roomId,
    senderId: user.id,
    senderName: profile.data.displayName || user.email,
    content,
    type,
    attachmentUrl,
    createdAt: new Date().toISOString(),
    readBy: [user.id],
  });
}

async function getMessages(roomId: string, limit: number = 50) {
  return await db.listDocuments("messages", {
    filters: {
      roomId,
      orderBy: "createdAt",
      order: "desc",
      limit,
    },
  });
}

async function markAsRead(messageId: string) {
  const user = await db.auth.getUser();
  const message = await db.getDocument("messages", messageId);

  if (message.data.readBy.includes(user.id)) {
    return message;
  }

  return await db.updateDocument("messages", messageId, {
    readBy: [...message.data.readBy, user.id],
  });
}

// Real-time
function watchMessages(roomId: string, callback: (message: any) => void) {
  return db.realtime.collection("messages", (event) => {
    if (event.document.data.roomId === roomId) {
      callback(event);
    }
  }, {
    filters: { roomId },
  });
}

// Presence
async function updatePresence(status: "online" | "away" | "offline") {
  const user = await db.auth.getUser();

  const existing = await db.listDocuments("presence", {
    filters: { userId: user.id },
  });

  const data = {
    userId: user.id,
    status,
    lastSeen: new Date().toISOString(),
  };

  if (existing.length > 0) {
    return await db.updateDocument("presence", existing[0].id, data);
  }

  return await db.createDocument("presence", data);
}

function watchPresence(userIds: string[], callback: (presence: any) => void) {
  return db.realtime.collection("presence", callback, {
    filters: {
      userId__in: userIds.join(","),
    },
  });
}

// Typing indicators
let typingTimeout: NodeJS.Timeout;

async function sendTypingIndicator(roomId: string) {
  const user = await db.auth.getUser();

  // Broadcast typing event
  await db.realtime.broadcast(`room:${roomId}:typing`, {
    userId: user.id,
    timestamp: Date.now(),
  });

  // Clear after 3 seconds
  clearTimeout(typingTimeout);
  typingTimeout = setTimeout(() => {
    db.realtime.broadcast(`room:${roomId}:typing:stop`, {
      userId: user.id,
    });
  }, 3000);
}

function watchTyping(roomId: string, callback: (data: any) => void) {
  return db.subscribe(`room:${roomId}:typing`, callback);
}

Key Concepts Demonstrated

  1. Real-time Messaging - Instant message delivery with watchers
  2. Room Types - Public, private, and direct message rooms
  3. Presence - Online/offline status tracking
  4. Typing Indicators - Real-time typing notifications
  5. Read Receipts - Track who has read messages

Next Steps

Real-time Features

Learn more about real-time

Todo App

Build a todo app