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.
Performance Optimization
Learn how to optimize your Cocobase application for maximum speed and efficiency.
Query Optimization
Always Use Limits
// BAD - Could return thousands of documents
const allPosts = await db.listDocuments("posts");
// GOOD - Limit results
const posts = await db.listDocuments("posts", {
filters: { limit: 20 },
});
Select Only Required Fields
// BAD - Returns all fields including large content
const posts = await db.listDocuments("posts");
// GOOD - Select only needed fields
const posts = await db.listDocuments("posts", {
filters: {
select: ["title", "author", "createdAt"],
limit: 20,
},
});
Use Efficient Filters
// BAD - Inefficient contains search on large fields
const posts = await db.listDocuments("posts", {
filters: { content__contains: "search term" },
});
// GOOD - Search on indexed fields first
const posts = await db.listDocuments("posts", {
filters: {
status: "published",
category: "technology",
title__contains: "search term",
limit: 20,
},
});
Good for small datasets with random page access:
async function getPage(page: number, pageSize: number = 20) {
return await db.listDocuments("posts", {
filters: {
limit: pageSize,
offset: (page - 1) * pageSize,
orderBy: "createdAt",
order: "desc",
},
});
}
// Usage
const page1 = await getPage(1);
const page5 = await getPage(5);
Better for large datasets and infinite scroll:
async function getNextPage(lastDocId?: string, pageSize: number = 20) {
const filters: any = {
limit: pageSize,
orderBy: "createdAt",
order: "desc",
};
if (lastDocId) {
// Get the timestamp of the last document
const lastDoc = await db.getDocument("posts", lastDocId);
filters.createdAt__lt = lastDoc.data.createdAt;
}
return await db.listDocuments("posts", { filters });
}
// Usage for infinite scroll
let lastId: string | undefined;
const firstPage = await getNextPage();
lastId = firstPage[firstPage.length - 1]?.id;
// Load more
const nextPage = await getNextPage(lastId);
Caching Strategies
In-Memory Caching
const cache = new Map<string, { data: any; expiry: number }>();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
async function getCachedDocument(collection: string, id: string) {
const cacheKey = `${collection}:${id}`;
const cached = cache.get(cacheKey);
if (cached && cached.expiry > Date.now()) {
return cached.data;
}
const doc = await db.getDocument(collection, id);
cache.set(cacheKey, {
data: doc,
expiry: Date.now() + CACHE_TTL,
});
return doc;
}
// Invalidate cache on updates
async function updateDocument(collection: string, id: string, data: any) {
await db.updateDocument(collection, id, data);
cache.delete(`${collection}:${id}`);
}
React Query / SWR Integration
// With React Query
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
function usePosts() {
return useQuery({
queryKey: ["posts"],
queryFn: () => db.listDocuments("posts", { filters: { limit: 20 } }),
staleTime: 5 * 60 * 1000, // 5 minutes
});
}
function useCreatePost() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: any) => db.createDocument("posts", data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["posts"] });
},
});
}
Flutter Caching
class CachedCocobase {
final Cocobase db;
final Map<String, CacheEntry> _cache = {};
final Duration cacheDuration;
CachedCocobase(this.db, {this.cacheDuration = const Duration(minutes: 5)});
Future<Document> getCachedDocument(String collection, String id) async {
final key = '$collection:$id';
final cached = _cache[key];
if (cached != null && !cached.isExpired) {
return cached.data;
}
final doc = await db.getDocument(collection, id);
_cache[key] = CacheEntry(doc, DateTime.now().add(cacheDuration));
return doc;
}
void invalidate(String collection, String id) {
_cache.remove('$collection:$id');
}
}
class CacheEntry {
final dynamic data;
final DateTime expiry;
CacheEntry(this.data, this.expiry);
bool get isExpired => DateTime.now().isAfter(expiry);
}
Batch Operations
Batch Creates
// BAD - Multiple network requests
for (const item of items) {
await db.createDocument("items", item);
}
// GOOD - Single batch request
const results = await db.batchCreateDocuments("items", items);
Batch Updates
// BAD - Multiple requests
for (const id of ids) {
await db.updateDocument("items", id, { status: "processed" });
}
// GOOD - Batch update
await db.batchUpdateDocuments("items", ids, { status: "processed" });
Parallel Fetching
// BAD - Sequential fetches
const user = await db.getDocument("users", userId);
const posts = await db.listDocuments("posts", { filters: { authorId: userId } });
const comments = await db.listDocuments("comments", { filters: { userId } });
// GOOD - Parallel fetches
const [user, posts, comments] = await Promise.all([
db.getDocument("users", userId),
db.listDocuments("posts", { filters: { authorId: userId, limit: 20 } }),
db.listDocuments("comments", { filters: { userId, limit: 50 } }),
]);
Real-time Optimization
Debounce Updates
import { debounce } from "lodash";
// Debounce rapid updates
const debouncedUpdate = debounce(
async (collection: string, id: string, data: any) => {
await db.updateDocument(collection, id, data);
},
500 // Wait 500ms after last change
);
// Use in form inputs
function handleInputChange(field: string, value: string) {
setFormData({ ...formData, [field]: value });
debouncedUpdate("drafts", draftId, { [field]: value });
}
Selective Real-time Subscriptions
// BAD - Watch entire collection
db.realtime.collection("messages", handleMessage);
// GOOD - Watch specific subset
db.realtime.collection("messages", handleMessage, {
filters: {
roomId: currentRoomId,
createdAt__gte: new Date().toISOString(),
},
});
Cleanup Subscriptions
// React component example
useEffect(() => {
const unsubscribe = db.realtime.collection("posts", (event) => {
// Handle event
});
// Clean up on unmount
return () => unsubscribe();
}, []);
Image and File Optimization
Lazy Loading Images
// Use native lazy loading
<img src={imageUrl} loading="lazy" alt="Description" />
// Or Intersection Observer
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target as HTMLImageElement;
img.src = img.dataset.src!;
observer.unobserve(img);
}
});
});
Compress Before Upload
async function compressImage(file: File, maxWidth: number = 1200): Promise<Blob> {
return new Promise((resolve) => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d")!;
const img = new Image();
img.onload = () => {
const ratio = Math.min(maxWidth / img.width, 1);
canvas.width = img.width * ratio;
canvas.height = img.height * ratio;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
canvas.toBlob((blob) => resolve(blob!), "image/jpeg", 0.8);
};
img.src = URL.createObjectURL(file);
});
}
// Use before upload
const compressedImage = await compressImage(originalFile);
await db.createDocumentWithFiles("uploads", { type: "image" }, { file: compressedImage });
Database Design Tips
// Normalized (more queries needed)
const post = await db.getDocument("posts", postId);
const author = await db.getDocument("users", post.data.authorId);
// Denormalized (single query)
const post = await db.getDocument("posts", postId);
// post.data.authorName already included
Use Computed Fields
// Store computed values at write time
await db.createDocument("orders", {
items: orderItems,
itemCount: orderItems.length, // Computed
totalPrice: orderItems.reduce((sum, item) => sum + item.price, 0), // Computed
});
// Query efficiently
const largeOrders = await db.listDocuments("orders", {
filters: {
totalPrice__gte: 100,
itemCount__gte: 5,
},
});
Track Query Times
async function timedQuery<T>(
name: string,
queryFn: () => Promise<T>
): Promise<T> {
const start = performance.now();
const result = await queryFn();
const duration = performance.now() - start;
console.log(`Query "${name}" took ${duration.toFixed(2)}ms`);
// Log slow queries
if (duration > 1000) {
console.warn(`Slow query detected: ${name}`);
}
return result;
}
// Usage
const posts = await timedQuery("list-posts", () =>
db.listDocuments("posts", { filters: { limit: 20 } })
);
Next Steps
Best Practices
General development best practices
Security
Secure your application
Querying
Advanced query techniques
Real-time
Real-time data sync