Globalcode

GlobalCode - Serverless Functions

GlobalCode is Globio's serverless function platform. Deploy backend logic with zero cold starts and edge performance.

🚀 Quick Start

import { GlobalCode } from '@stanlink/globio'
 
const functions = new GlobalCode({
  apiKey: 'your-api-key',
  projectId: 'your-project-id'
})
 
// Call a function
const result = await functions.httpsCallable('calculateScore')({
  playerId: 'player123',
  gameData: { kills: 5, time: 300 }
})

📝 Writing Functions

Basic Function

// functions/calculateScore.js
export default async function calculateScore(data, context) {
  const { playerId, gameData } = data
  
  // Calculate score based on game data
  const baseScore = gameData.kills * 100
  const timeBonus = Math.max(0, 600 - gameData.time)
  const totalScore = baseScore + timeBonus
  
  return {
    playerId,
    score: totalScore,
    breakdown: {
      kills: baseScore,
      timeBonus: timeBonus
    }
  }
}

Database Integration

// functions/updateLeaderboard.js
import { GlobalDoc } from '@stanlink/globio'
 
export default async function updateLeaderboard(data, context) {
  const { playerId, score } = data
  const db = new GlobalDoc({ apiKey: context.apiKey })
  
  // Update player's best score
  await db.collection('leaderboard').doc(playerId).set({
    playerId,
    bestScore: score,
    lastUpdated: new Date()
  }, { merge: true })
  
  // Get player's rank
  const betterPlayers = await db.collection('leaderboard')
    .where('bestScore', '>', score)
    .get()
  
  const rank = betterPlayers.size + 1
  
  return { rank, score }
}

🎮 Gaming Examples

Matchmaking Function

// functions/findMatch.js
export default async function findMatch(data, context) {
  const { playerId, skillLevel, gameMode } = data
  const db = new GlobalDoc({ apiKey: context.apiKey })
  
  // Find players looking for match
  const waitingPlayers = await db.collection('matchmaking')
    .where('gameMode', '==', gameMode)
    .where('skillLevel', '>=', skillLevel - 100)
    .where('skillLevel', '<=', skillLevel + 100)
    .where('status', '==', 'waiting')
    .limit(3)
    .get()
  
  if (waitingPlayers.size >= 3) {
    // Create match
    const matchId = `match_${Date.now()}`
    const players = [playerId, ...waitingPlayers.docs.map(doc => doc.id)]
    
    await db.collection('matches').doc(matchId).set({
      players,
      gameMode,
      status: 'starting',
      createdAt: new Date()
    })
    
    return { matchId, players }
  } else {
    // Add to waiting queue
    await db.collection('matchmaking').doc(playerId).set({
      playerId,
      skillLevel,
      gameMode,
      status: 'waiting',
      joinedAt: new Date()
    })
    
    return { status: 'waiting' }
  }
}

Anti-Cheat Validation

// functions/validateScore.js
export default async function validateScore(data, context) {
  const { playerId, score, gameData, timestamp } = data
  
  // Basic validation checks
  const maxPossibleScore = gameData.timeLimit * 10 // 10 points per second max
  const timeDiff = Date.now() - timestamp
  
  if (score > maxPossibleScore) {
    return { valid: false, reason: 'Score too high' }
  }
  
  if (timeDiff > 60000) { // 1 minute tolerance
    return { valid: false, reason: 'Submission too late' }
  }
  
  // Advanced checks could include:
  // - Pattern analysis
  // - Historical player data
  // - Machine learning models
  
  return { valid: true, score }
}

Tournament Management

// functions/manageTournament.js
export default async function manageTournament(data, context) {
  const { tournamentId, action, playerId } = data
  const db = new GlobalDoc({ apiKey: context.apiKey })
  
  const tournament = await db.collection('tournaments').doc(tournamentId).get()
  const tournamentData = tournament.data()
  
  switch (action) {
    case 'join':
      if (tournamentData.players.length < tournamentData.maxPlayers) {
        await tournament.ref.update({
          players: [...tournamentData.players, playerId],
          updatedAt: new Date()
        })
        return { success: true, message: 'Joined tournament' }
      }
      return { success: false, message: 'Tournament is full' }
      
    case 'start':
      if (tournamentData.players.length >= tournamentData.minPlayers) {
        // Create bracket
        const bracket = generateBracket(tournamentData.players)
        await tournament.ref.update({
          status: 'active',
          bracket,
          startedAt: new Date()
        })
        return { success: true, bracket }
      }
      return { success: false, message: 'Not enough players' }
      
    case 'advance':
      // Handle match results and advance winners
      const updatedBracket = advanceWinner(tournamentData.bracket, data.matchResult)
      await tournament.ref.update({
        bracket: updatedBracket,
        updatedAt: new Date()
      })
      return { success: true, bracket: updatedBracket }
  }
}

🔄 Triggers and Events

Database Triggers

// functions/onPlayerUpdate.js
// Triggered when a player document is updated
export default async function onPlayerUpdate(change, context) {
  const { before, after } = change
  const playerId = context.params.playerId
  
  // Check if level increased
  if (after.level > before.level) {
    // Award level-up bonus
    await after.ref.update({
      coins: after.coins + (after.level * 10),
      lastLevelUp: new Date()
    })
    
    // Send notification
    await sendNotification(playerId, {
      title: 'Level Up!',
      body: `Congratulations! You reached level ${after.level}`,
      data: { type: 'level_up', level: after.level }
    })
  }
}

Scheduled Functions

// functions/dailyRewards.js
// Runs every day at midnight
export default async function dailyRewards(context) {
  const db = new GlobalDoc({ apiKey: context.apiKey })
  
  // Get all active players
  const activePlayers = await db.collection('players')
    .where('lastLogin', '>=', new Date(Date.now() - 7 * 24 * 60 * 60 * 1000))
    .get()
  
  const batch = db.batch()
  
  activePlayers.forEach(doc => {
    const player = doc.data()
    const dailyReward = calculateDailyReward(player.level, player.streak)
    
    batch.update(doc.ref, {
      coins: player.coins + dailyReward.coins,
      gems: player.gems + dailyReward.gems,
      streak: player.streak + 1,
      lastDailyReward: new Date()
    })
  })
  
  await batch.commit()
  console.log(`Processed daily rewards for ${activePlayers.size} players`)
}

🔒 Security and Validation

Authentication Context

// functions/secureFunction.js
export default async function secureFunction(data, context) {
  // Check if user is authenticated
  if (!context.auth) {
    throw new Error('Authentication required')
  }
  
  const userId = context.auth.uid
  const userClaims = context.auth.token
  
  // Check user permissions
  if (!userClaims.admin && data.adminAction) {
    throw new Error('Admin privileges required')
  }
  
  // Proceed with function logic
  return { success: true, userId }
}

Input Validation

// functions/validateInput.js
import Joi from 'joi'
 
const schema = Joi.object({
  playerId: Joi.string().required(),
  score: Joi.number().integer().min(0).max(1000000).required(),
  gameMode: Joi.string().valid('classic', 'blitz', 'tournament').required()
})
 
export default async function validateInput(data, context) {
  // Validate input data
  const { error, value } = schema.validate(data)
  
  if (error) {
    throw new Error(`Invalid input: ${error.details[0].message}`)
  }
  
  // Process validated data
  return processGameScore(value)
}

📊 Performance and Monitoring

Function Metrics

// functions/monitoredFunction.js
export default async function monitoredFunction(data, context) {
  const startTime = Date.now()
  
  try {
    // Function logic here
    const result = await processData(data)
    
    // Log success metrics
    console.log('Function completed', {
      duration: Date.now() - startTime,
      success: true,
      dataSize: JSON.stringify(data).length
    })
    
    return result
  } catch (error) {
    // Log error metrics
    console.error('Function failed', {
      duration: Date.now() - startTime,
      success: false,
      error: error.message
    })
    
    throw error
  }
}

Caching Strategies

// functions/cachedFunction.js
const cache = new Map()
const CACHE_TTL = 5 * 60 * 1000 // 5 minutes
 
export default async function cachedFunction(data, context) {
  const cacheKey = `${data.type}_${data.id}`
  const cached = cache.get(cacheKey)
  
  // Return cached result if valid
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.data
  }
  
  // Compute new result
  const result = await expensiveComputation(data)
  
  // Cache the result
  cache.set(cacheKey, {
    data: result,
    timestamp: Date.now()
  })
  
  return result
}

🚀 Deployment and Testing

Local Development

// Test functions locally
import { GlobalCode } from '@stanlink/globio'
 
const functions = new GlobalCode({
  apiKey: 'test-key',
  projectId: 'test-project',
  emulator: true // Use local emulator
})
 
// Test function call
const result = await functions.httpsCallable('calculateScore')({
  playerId: 'test-player',
  gameData: { kills: 5, time: 300 }
})
 
console.log('Test result:', result)

Function Configuration

// globio.config.js
export default {
  functions: {
    calculateScore: {
      memory: '256MB',
      timeout: '30s',
      regions: ['us-east1', 'europe-west1']
    },
    dailyRewards: {
      schedule: '0 0 * * *', // Daily at midnight
      memory: '512MB',
      timeout: '300s'
    }
  }
}

💡 Best Practices

Error Handling

export default async function robustFunction(data, context) {
  try {
    // Validate input
    if (!data.playerId) {
      throw new Error('Player ID is required')
    }
    
    // Process data
    const result = await processPlayerData(data)
    
    return { success: true, data: result }
  } catch (error) {
    console.error('Function error:', error)
    
    // Return structured error response
    return {
      success: false,
      error: {
        code: error.code || 'UNKNOWN_ERROR',
        message: error.message
      }
    }
  }
}

Resource Management

// Efficient database operations
export default async function efficientFunction(data, context) {
  const db = new GlobalDoc({ apiKey: context.apiKey })
  
  // Use batch operations for multiple writes
  const batch = db.batch()
  
  data.updates.forEach(update => {
    const docRef = db.collection('players').doc(update.playerId)
    batch.update(docRef, update.data)
  })
  
  await batch.commit()
  
  // Close connections properly
  await db.terminate()
}

Next: Learn about GlobioID Authentication