Managing Global Collection Data
This guide explains how to manage collection data in Agent8. Collections provide a structured way to store and retrieve persistent data—like chat histories, leaderboards, or game records—that can be queried and paginated as needed.
Understanding Collections
Collections in Agent8 are powered by Firestore, offering a simple interface to add, retrieve, update, and delete items. Each collection is uniquely identified and can hold multiple items, making it ideal for scalable data management.
Collections are best used for data that requires querying or pagination, such as chat logs, rankings, or other game records.
Server API
-
$global.getCollectionItems(collectionId: string, options?: CollectionOptions): Promise<Item[]>
Retrieves multiple items from a collection based on filtering, sorting, and pagination options. -
$global.getCollectionItem(collectionId: string, itemId: string): Promise<Item>
Retrieves a single item from a collection using its unique ID. -
$global.countCollectionItems(collectionId: string, options?: CollectionOptions): Promise<number>
Retrieves number of items from a collection based on filtering, sorting, and pagination options. -
$global.addCollectionItem(collectionId: string, item: any): Promise<Item>
Adds a new item to the specified collection and returns the added item with its unique identifier. -
$global.updateCollectionItem(collectionId: string, item: any): Promise<Item>
Updates an existing item in a collection and returns the updated item. -
$global.deleteCollectionItem(collectionId: string, itemId: string): Promise<{ __id: string }>
Deletes an item from the collection and returns a confirmation containing the deleted item’s ID. -
$global.deleteCollection(collectionId: string): Promise<string>
Deletes the collection and returns a confirmation containing the deleted collection ID.
class Server {
async addCollectionItem(collectionId, item) {
return await $global.addCollectionItem(collectionId, item);
}
async updateCollectionItem(collectionId, item) {
return await $global.updateCollectionItem(collectionId, item);
}
async deleteCollectionItem(collectionId, itemId) {
return await $global.deleteCollectionItem(collectionId, itemId);
}
async getCollectionItems(collectionId, options = {}) {
return await $global.getCollectionItems(collectionId, options);
}
async getCollectionItem(collectionId, itemId) {
return await $global.getCollectionItem(collectionId, itemId);
}
async countCollectionItems(collectionId, options = {}) {
return await $global.countCollectionItems(collectionId, options);
}
}Client API
subscriptions
server.subscribeGlobalCollection(collectionId: string, ({ items, changes }: { items: any[], changes: { op: 'add'|'update'|'delete'|'deleteAll', items: any[]} }) => {}): UnsubscribeFunction
hooks
const { items } = useGlobalCollection(collectionId)
Subscribe to Global Collection Updates
import { useGameServer } from "@agent8/gameserver";
import { useEffect } from "react";
function CollectionComponent() {
const { connected, server } = useGameServer();
const collectionId = "my-collection";
useEffect(() => {
if (!connected) return;
const unsubscribe = server.subscribeGlobalCollection(
collectionId,
({ items, changes }) => {
console.log("all items", items);
console.log("Updated items:", changes.items);
// Handle different operations
if (changes.op === "add") {
// Handle added items
} else if (changes.op === "update") {
// Handle updated items
} else if (changes.op === "delete") {
// Handle deleted items
}
}
);
// Cleanup subscription when component unmounts
return () => unsubscribe();
}, [connected, server, collectionId]);
return <div>Collection Component</div>;
}Sync Collection with React Hooks
To synchronize collections in real-time using hooks:
import { useGlobalCollection } from "@agent8/gameserver";
function CollectionDisplay() {
const collectionId = "my-collection";
const { items } = useGlobalCollection(collectionId);
return (
<div>
<h2>Collection Items</h2>
<ul>
{items.map((item) => (
<li key={item.__id}>{JSON.stringify(item)}</li>
))}
</ul>
</div>
);
}The useGameCollection hook automatically handles subscriptions and
unsubscriptions, making it the recommended approach for React applications.
Example 1: Chat
class Server {
async sendChat(message) {
const chatItem = {
sender: $sender.account,
message,
timestamp: Date.now(),
};
return await $global.addCollectionItem("chats", chatItem);
}
async deleteChat(id) {
const chat = await $global.getCollectionItem("chats", id);
if (chat.sender !== $sender.account) {
return { error: "Not authorized to delete this message" };
}
return await $global.deleteCollectionItem("chats", id);
}
async getChats() {
return await $global.getCollectionItems("chats");
}
}import { useGameServer, useGlobalCollection } from "@agent8/gameserver";
import { useState, useEffect } from "react";
export default function Chat() {
const { connected, server } = useGameServer();
const { items: chats } = useGlobalCollection("chats");
const [message, setMessage] = useState("");
useEffect(() => {
if (!connected) return;
// Optional: Perform additional setup with the collection subscription
const unsubscribe = server.subscribeGlobalCollection(
"chats",
({ items, changes }) => {
// Custom handling if needed beyond useGameCollection
}
);
return () => unsubscribe();
}, [connected, server]);
if (!connected) return <p>Loading...</p>;
const handleSend = async () => {
if (message.trim()) {
await server.remoteFunction("sendChat", [message]);
setMessage("");
}
};
const handleDelete = async (id) => {
await server.remoteFunction("deleteChat", [id]);
};
return (
<div>
<div>
{chats.map((chat) => (
<div key={chat.__id}>
<span>{chat.sender}: </span>
<span>{chat.message}</span>
<button onClick={() => handleDelete(chat.__id)}>Delete</button>
</div>
))}
</div>
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleSend()}
/>
<button onClick={handleSend}>Send</button>
</div>
);
}