Globaldoc

GlobalDoc - Document Database

GlobalDoc is Globio's Firebase-compatible document database service. Store, query, and sync JSON documents with real-time updates - all at 99% less cost than Firestore.

🚀 Quick Start

import { GlobalDoc } from '@stanlink/globio'
 
const globio = new GlobalDoc({
  apiKey: 'your-api-key',
  projectId: 'your-project-id'
})
 
// Add a document
const users = globio.collection('users')
const docRef = await users.add({
  name: 'John Doe',
  email: 'john@example.com',
  level: 5,
  createdAt: new Date()
})
 
console.log('Document added with ID:', docRef.id)

📚 Core Concepts

Collections

Collections are containers for documents. Think of them as tables in a traditional database.

// Get a collection reference
const users = globio.collection('users')
const posts = globio.collection('posts')
const gameStats = globio.collection('gameStats')

Documents

Documents are JSON objects stored in collections. Each document has a unique ID.

// Add a document with auto-generated ID
const docRef = await users.add({
  name: 'Alice',
  score: 1500,
  achievements: ['first-win', 'level-10']
})
 
// Add a document with custom ID
await users.doc('player123').set({
  name: 'Bob',
  score: 2000,
  lastLogin: new Date()
})

🔍 Querying Data

Simple Queries

// Get all documents
const snapshot = await users.get()
snapshot.forEach(doc => {
  console.log(doc.id, doc.data())
})
 
// Query with conditions
const highScorers = await users
  .where('score', '>=', 1000)
  .get()
 
// Order and limit results
const topPlayers = await users
  .orderBy('score', 'desc')
  .limit(10)
  .get()

Complex Queries

// Multiple conditions
const activeHighScorers = await users
  .where('score', '>=', 1000)
  .where('status', '==', 'active')
  .where('lastLogin', '>=', new Date('2024-01-01'))
  .orderBy('score', 'desc')
  .limit(50)
  .get()
 
// Array queries
const achievementHunters = await users
  .where('achievements', 'array-contains', 'rare-collector')
  .get()
 
// In queries
const specificUsers = await users
  .where('userId', 'in', ['user1', 'user2', 'user3'])
  .get()

📝 Writing Data

Adding Documents

// Add with auto-generated ID
const docRef = await users.add({
  name: 'Charlie',
  level: 1
})
 
// Add with custom ID
await users.doc('charlie123').set({
  name: 'Charlie',
  level: 1
})

Updating Documents

// Update specific fields
await users.doc('charlie123').update({
  level: 2,
  lastUpdated: new Date()
})
 
// Set with merge (updates existing fields, adds new ones)
await users.doc('charlie123').set({
  newField: 'value'
}, { merge: true })

Deleting Documents

// Delete a document
await users.doc('charlie123').delete()
 
// Delete a field
await users.doc('alice123').update({
  temporaryField: null // This deletes the field
})

⚡ Real-time Updates

Listen to Documents

// Listen to a single document
const unsubscribe = users.doc('player123').onSnapshot(doc => {
  if (doc.exists) {
    console.log('Player data:', doc.data())
  }
})
 
// Stop listening
unsubscribe()

Listen to Collections

// Listen to all users
const unsubscribe = users.onSnapshot(snapshot => {
  snapshot.forEach(doc => {
    console.log('User updated:', doc.data())
  })
})
 
// Listen to query results
const unsubscribe = users
  .where('online', '==', true)
  .onSnapshot(snapshot => {
    console.log('Online users:', snapshot.size)
  })

🔄 Batch Operations

Batch Writes

const batch = globio.batch()
 
// Add multiple operations to batch
batch.set(users.doc('user1'), { name: 'User 1', score: 100 })
batch.update(users.doc('user2'), { score: 200 })
batch.delete(users.doc('user3'))
 
// Commit all operations atomically
await batch.commit()

Transactions

await globio.runTransaction(async (transaction) => {
  // Read data
  const userDoc = await transaction.get(users.doc('player123'))
  const currentScore = userDoc.data().score
  
  // Write data based on read
  transaction.update(users.doc('player123'), {
    score: currentScore + 100,
    lastUpdated: new Date()
  })
})

🎮 Gaming Examples

Player Profiles

// Create player profile
await users.doc(playerId).set({
  name: playerName,
  level: 1,
  experience: 0,
  coins: 100,
  inventory: [],
  achievements: [],
  stats: {
    gamesPlayed: 0,
    gamesWon: 0,
    totalPlayTime: 0
  },
  createdAt: new Date(),
  lastLogin: new Date()
})
 
// Level up player
await globio.runTransaction(async (transaction) => {
  const playerDoc = await transaction.get(users.doc(playerId))
  const player = playerDoc.data()
  
  transaction.update(users.doc(playerId), {
    level: player.level + 1,
    experience: 0, // Reset XP for new level
    coins: player.coins + 50 // Level up bonus
  })
})

Leaderboards

// Get top 10 players by score
const leaderboard = await users
  .orderBy('score', 'desc')
  .limit(10)
  .get()
 
// Get player's rank
const playersAbove = await users
  .where('score', '>', playerScore)
  .get()
const playerRank = playersAbove.size + 1

Game Sessions

const sessions = globio.collection('gameSessions')
 
// Start game session
const sessionRef = await sessions.add({
  playerId: playerId,
  gameMode: 'battle-royale',
  startTime: new Date(),
  status: 'active',
  score: 0,
  kills: 0
})
 
// Update session during game
await sessionRef.update({
  score: currentScore,
  kills: currentKills,
  lastUpdate: new Date()
})
 
// End session
await sessionRef.update({
  endTime: new Date(),
  status: 'completed',
  duration: endTime - startTime
})

🔒 Security Rules

Basic Rules

// Only authenticated users can read/write their own data
const userDoc = users.doc(currentUser.uid)
await userDoc.set(userData) // ✅ Allowed
await users.doc('otherUser').set(data) // ❌ Denied

Collection-level Security

// Public leaderboards (read-only for all users)
const leaderboard = globio.collection('leaderboard')
const topPlayers = await leaderboard.get() // ✅ Anyone can read
 
// Private user data
const privateData = globio.collection('privateUserData')
// Only the user can access their own private data

📊 Performance Tips

Indexing

// Compound queries require indexes
// This query needs an index on (status, score)
const activeTopPlayers = await users
  .where('status', '==', 'active')
  .orderBy('score', 'desc')
  .limit(10)
  .get()

Pagination

// Efficient pagination
let lastDoc = null
const pageSize = 20
 
async function getNextPage() {
  let query = users
    .orderBy('createdAt')
    .limit(pageSize)
  
  if (lastDoc) {
    query = query.startAfter(lastDoc)
  }
  
  const snapshot = await query.get()
  lastDoc = snapshot.docs[snapshot.docs.length - 1]
  
  return snapshot.docs.map(doc => doc.data())
}

Caching

// Cache frequently accessed data
const cache = new Map()
 
async function getCachedUser(userId) {
  if (cache.has(userId)) {
    return cache.get(userId)
  }
  
  const doc = await users.doc(userId).get()
  const userData = doc.data()
  
  cache.set(userId, userData)
  return userData
}

🚀 Migration from Firestore

Code Changes

// Before (Firestore)
import { getFirestore, collection, addDoc } from 'firebase/firestore'
const db = getFirestore()
const usersRef = collection(db, 'users')
await addDoc(usersRef, userData)
 
// After (GlobalDoc)
import { GlobalDoc } from '@stanlink/globio'
const globio = new GlobalDoc({ apiKey: 'your-key' })
const users = globio.collection('users')
await users.add(userData)

Data Migration

// Export from Firestore and import to GlobalDoc
async function migrateCollection(collectionName) {
  // Export from Firestore
  const firestoreSnapshot = await firestoreDb.collection(collectionName).get()
  
  // Import to GlobalDoc
  const batch = globio.batch()
  firestoreSnapshot.forEach(doc => {
    batch.set(globio.collection(collectionName).doc(doc.id), doc.data())
  })
  
  await batch.commit()
  console.log(`Migrated ${collectionName}`)
}

💡 Best Practices

Data Structure

// ✅ Good: Flat structure
{
  userId: 'player123',
  name: 'John',
  level: 5,
  stats_gamesPlayed: 100,
  stats_gamesWon: 75
}
 
// ❌ Avoid: Deep nesting
{
  userId: 'player123',
  profile: {
    personal: {
      name: 'John',
      details: {
        level: 5
      }
    }
  }
}

Field Names

// ✅ Use consistent naming
{
  createdAt: new Date(),
  updatedAt: new Date(),
  isActive: true,
  playerCount: 4
}
 
// ❌ Inconsistent naming
{
  created_at: new Date(),
  UpdatedTime: new Date(),
  active: true,
  numPlayers: 4
}

🔧 Advanced Features

Subcollections

// User's game sessions as subcollection
const userSessions = users.doc('player123').collection('sessions')
await userSessions.add({
  gameMode: 'ranked',
  score: 1500,
  date: new Date()
})

Array Operations

// Add to array
await users.doc('player123').update({
  achievements: arrayUnion('new-achievement')
})
 
// Remove from array
await users.doc('player123').update({
  achievements: arrayRemove('old-achievement')
})

Increment Operations

// Increment score atomically
await users.doc('player123').update({
  score: increment(100),
  gamesPlayed: increment(1)
})

Next: Learn about Real-time Features