Skip to content

Edge Functions API

Last Updated: 2025-02-06


Overview

Edge Functions provide aggregated endpoints that reduce REST request volume by combining multiple database queries into single responses. All Edge Functions include optional caching and maintain tenant isolation.

Benefits:

  • ✅ Reduces REST request volume (5+ queries → 1 request)
  • ✅ Optional caching (30-60s TTL per tenant)
  • ✅ Maintains tenant isolation
  • ✅ Automatic fallback to direct Supabase queries
  • ✅ Backward compatible (existing queries still work)

Endpoints

Endpoint: GET /functions/v1/get-dashboard-snapshot-v2

Description: Unified dashboard data aggregation endpoint combining all major dashboard data sources into a single response. Supports lightweight mode for KPI-only dashboards and pagination for large datasets.

Aggregates:

  • Zones with utilization
  • Inventory items with metrics and stock levels
  • Active pick lists
  • Active shipments
  • Active drivers
  • Safety metrics and recent incidents
  • Import jobs metrics

Query Parameters:

  • lightweight=true (optional) — Return only metrics and counts, no full item lists (60-70% size reduction)
  • page=1 (optional) — Page number for pagination (default: 1)
  • pageSize=50 (optional) — Items per page (default: 50, max: 200)

Caching: 60 seconds TTL per tenant (cache key includes query parameters)

Response (Full Mode):

json
{
  "tenant_id": "uuid",
  "timestamp": "2025-02-06T10:00:00Z",
  "cached": false,
  "zones": {
    "total": 15,
    "items": [
      {
        "id": "uuid",
        "code": "A-01",
        "name": "Zone A",
        "x": 10,
        "y": 20,
        "z": 0,
        "max_capacity": 100
      }
    ]
  },
  "inventory": {
    "items": [...],
    "metrics": {
      "total_items": 150,
      "low_stock_count": 5,
      "total_stock_value": 0
    },
    "stock_levels": [
      {
        "item_id": "uuid",
        "quantity": 45
      }
    ]
  },
  "pick_lists": {
    "active": 5,
    "items": [...]
  },
  "shipments": {
    "active": 8,
    "items": [...]
  },
  "drivers": {
    "active": 3,
    "items": [...]
  },
  "safety": {
    "metrics": {
      "total_incidents": 12,
      "critical_incidents": 2,
      "open_incidents": 5,
      "incidents_by_category": {
        "slip": 3,
        "fall": 2
      }
    },
    "recent_incidents": [...]
  },
  "import": {
    "metrics": {
      "total_jobs": 10,
      "completed_jobs": 8,
      "failed_jobs": 1,
      "total_imported": 1250
    },
    "recent_jobs": [...]
  },
  "pagination": {
    "page": 1,
    "pageSize": 50,
    "hasMore": false
  }
}

Response (Lightweight Mode):

json
{
  "tenant_id": "uuid",
  "timestamp": "2025-02-06T10:00:00Z",
  "cached": false,
  "zones": {
    "total": 15
  },
  "inventory": {
    "metrics": {
      "total_items": 150,
      "low_stock_count": 5,
      "total_stock_value": 0
    }
  },
  "pick_lists": {
    "active": 5
  },
  "shipments": {
    "active": 8
  },
  "drivers": {
    "active": 3
  },
  "safety": {
    "metrics": {
      "total_incidents": 12,
      "critical_incidents": 2,
      "open_incidents": 5,
      "incidents_by_category": {}
    }
  },
  "import": {
    "metrics": {
      "total_jobs": 10,
      "completed_jobs": 8,
      "failed_jobs": 1,
      "total_imported": 1250
    }
  },
  "pagination": {
    "page": 1,
    "pageSize": 50,
    "hasMore": false
  }
}

Usage:

typescript
import { fetchDashboardSnapshotV2 } from "@/lib/api-edge";

// Full data mode
const data = await fetchDashboardSnapshotV2({
  useCache: true,
  lightweight: false,
  page: 1,
  pageSize: 50
});

// Lightweight mode (KPI dashboards)
const data = await fetchDashboardSnapshotV2({
  useCache: true,
  lightweight: true
});

React Hook:

typescript
import { useDashboardSnapshotV2 } from "@/lib/queries";

// Full data
const { data, isLoading } = useDashboardSnapshotV2({
  lightweight: false,
  refetchInterval: 60 * 1000
});

// Lightweight mode
const { data, isLoading } = useDashboardSnapshotV2({
  lightweight: true
});

Performance:

  • Size Reduction: 60-90% smaller responses (500KB → 200KB full / 50KB lightweight)
  • Request Reduction: Replaces 15-20+ individual REST requests with 1 aggregated request
  • Compression: Automatic gzip/deflate compression
  • Cache Hit Rate: ~70% for frequently accessed dashboards

Fallback: Falls back to admin-dashboard-summary Edge Function or direct Supabase queries if unavailable.


2. Admin Dashboard Summary

Endpoint: GET /functions/v1/admin-dashboard-summary

Description: Aggregates admin dashboard data into a single response. Note: Consider using get-dashboard-snapshot-v2 for new implementations.

Aggregates:

  • Zones with utilization
  • Inventory items with health metrics
  • Active pick lists
  • Recent shipments with stats
  • System settings

Caching: 30 seconds TTL per tenant

Response:

json
{
  "tenant_id": "uuid",
  "timestamp": "2025-02-03T10:00:00Z",
  "zones": [...],
  "inventory": {
    "items": [...],
    "total_items": 150,
    "health": {
      "score": 85,
      "fast_movers": 45,
      "slow_stock": 10,
      "dead_stock": 2,
      "critical_items": 3
    }
  },
  "pick_lists": {
    "active": [...],
    "active_count": 5
  },
  "shipments": {
    "recent": [...],
    "stats": {
      "total": 20,
      "active": 8,
      "in_transit": 3,
      "completed_today": 5
    }
  },
  "settings": {...}
}

Usage:

typescript
import { fetchAdminDashboardSummary } from "@/lib/api-edge";

const data = await fetchAdminDashboardSummary();

Fallback: Falls back to get-dashboard-snapshot Edge Function or direct Supabase queries.


2. Calculate Predictive Safety Scores

Endpoint: POST /functions/v1/calculate-predictive-safety-scores

Description: Computes predictive risk scores for entities (zones, drivers, workers, equipment) based on incidents, near misses, shift hours, and workload.

Request Body:

json
{
  "entity_type": "zone",  // Optional: 'zone', 'driver', 'worker', 'equipment'
  "entity_id": "uuid",    // Optional: specific entity ID
  "period_days": 30       // Optional: lookback period (default: 30)
}

Query Parameters (GET):

  • entity_type (optional)
  • entity_id (optional)
  • period_days (optional, default: 30)

Response:

json
{
  "success": true,
  "tenant_id": "uuid",
  "scores_computed": 15,
  "scores": [
    {
      "entity_type": "zone",
      "entity_id": "uuid",
      "score": 65.5,
      "confidence": 0.75,
      "inputs": {
        "incident_count": 3,
        "near_miss_count": 8,
        "shift_hours": 0,
        "workload": 12,
        "period_days": 30
      }
    }
  ],
  "model": {
    "weight_incident": 2.0,
    "weight_near_miss": 1.5,
    "weight_shift_hours": 0.3,
    "weight_load": 0.2
  }
}

Usage:

typescript
import { calculatePredictiveSafetyScores } from "@/lib/api-edge";

// Calculate all scores
const result = await calculatePredictiveSafetyScores();

// Calculate for specific entity
const result = await calculatePredictiveSafetyScores('zone', zoneId, 30);

See: Predictive Intelligence Documentation


3. Assign Risk Clusters (Phase 2)

Endpoint: POST /functions/v1/assign-risk-clusters

Description: Groups entities into risk clusters based on their predictive scores. Creates clusters for different severity ranges (Critical, High, Medium, Low).

Request Body:

json
{
  "entity_type": "zone",  // Optional: 'zone', 'driver', 'worker', 'equipment'
  "min_score": 0,         // Optional: minimum score threshold (default: 0)
  "max_score": 100        // Optional: maximum score threshold (default: 100)
}

Response:

json
{
  "success": true,
  "tenant_id": "uuid",
  "clusters_created": 4,
  "entities_assigned": 25,
  "clusters": [
    {
      "id": "uuid",
      "cluster_name": "Critical Risk",
      "severity": 85.5,
      "description": "Entities with very high risk scores (70-100)",
      "entity_count": 8
    }
  ]
}

Usage:

typescript
import { assignRiskClusters } from "@/lib/api-edge";

// Assign all entities to clusters
const result = await assignRiskClusters();

// Assign only zones
const result = await assignRiskClusters('zone');

See: Predictive Intelligence Documentation - Phase 2


4. Check Compliance Rules (Phase 4)

Endpoint: POST /functions/v1/check-compliance-rules

Description: Automatically scans safety data (predictive scores, incidents, near misses) to check compliance against configured safety regulations (OSHA, EU-OSHA, etc.).

Request Body:

json
{
  "entity_type": "zone",  // Optional: 'zone', 'driver', 'worker', 'equipment'
  "entity_id": "uuid",    // Optional: specific entity ID
  "jurisdiction": "US"    // Optional: 'US', 'EU', 'Global'
}

Response:

json
{
  "success": true,
  "tenant_id": "uuid",
  "events_created": 5,
  "summary": {
    "total_regulations": 12,
    "compliant_count": 8,
    "warning_count": 3,
    "breach_count": 1,
    "last_check": "2025-02-05T10:00:00Z"
  },
  "events": [
    {
      "regulation_id": "uuid",
      "entity_type": "zone",
      "entity_id": "uuid",
      "status": "breach"
    }
  ]
}

Usage:

typescript
import { checkComplianceRules } from "@/lib/api-edge";

// Check all compliance
const result = await checkComplianceRules();

// Check specific entity
const result = await checkComplianceRules('zone', zoneId);

// Check specific jurisdiction
const result = await checkComplianceRules(undefined, undefined, 'US');

See: Compliance Automation Documentation


5. Auto-Assign Training (Phase 6)

Endpoint: POST /functions/v1/auto-assign-training

Description: Automatically assigns training courses to users based on predictive risk scores. Triggers when risk scores exceed course thresholds.

Request Body:

json
{
  "entity_type": "worker",  // Optional: 'zone', 'driver', 'worker', 'equipment'
  "entity_id": "uuid",      // Optional: specific entity ID
  "min_risk_score": 50      // Optional: minimum risk score threshold (default: 0)
}

Response:

json
{
  "success": true,
  "tenant_id": "uuid",
  "assignments_created": 5,
  "courses_checked": 12,
  "scores_checked": 8,
  "assignments": [
    {
      "tenant_id": "uuid",
      "course_id": "uuid",
      "user_id": "uuid",
      "assignment_type": "risk_based",
      "assignment_reason": "High risk score (75) for worker uuid",
      "status": "assigned"
    }
  ]
}

Usage:

typescript
import { autoAssignTraining } from "@/lib/api-edge";

// Auto-assign for all high-risk entities
const result = await autoAssignTraining();

// Auto-assign for specific entity type
const result = await autoAssignTraining('worker');

// Auto-assign with minimum risk score
const result = await autoAssignTraining(undefined, undefined, 60);

See: LMS Training Integration Documentation


6. Safety KPIs

Endpoint: GET /functions/v1/safety-kpis?days=30

Description: Aggregates safety KPI calculations into a single response.

Query Parameters:

  • days (optional): Number of days for date range (default: 30)

Aggregates:

  • Total Safety Reports (TSR)
  • Critical incidents count
  • Open actions count
  • Overdue actions count
  • Checklist compliance percentage
  • Recent incidents (last 10)
  • Distribution by category and severity

Caching: 60 seconds TTL per tenant

Response:

json
{
  "tenant_id": "uuid",
  "timestamp": "2025-02-03T10:00:00Z",
  "date_range_days": 30,
  "kpis": {
    "tsr": 25,
    "critical_incidents": 3,
    "open_actions": 12,
    "overdue_actions": 2,
    "checklist_compliance": 85
  },
  "distribution": {
    "by_category": {
      "injury": 5,
      "near_miss": 10,
      "hazard": 8
    },
    "by_severity": {
      "low": 15,
      "medium": 8,
      "high": 2
    }
  },
  "recent_incidents": [...]
}

Usage:

typescript
import { fetchSafetyKPIs } from "@/lib/api-edge";

const data = await fetchSafetyKPIs(30); // Last 30 days

Fallback: Falls back to direct Supabase queries for incidents, actions, and checklist submissions.


7. Process Import Job (Data Import Hub)

Endpoint: POST /functions/v1/process-import-job

Description: Processes an import job, parses file data, and prepares records for mapping and validation.

Request Body:

json
{
  "job_id": "uuid"
}

Response:

json
{
  "success": true,
  "job_id": "uuid",
  "file_type": "csv",
  "stats": {
    "total_rows": 100,
    "valid_rows": 95,
    "invalid_rows": 5
  },
  "message": "Job processing started"
}

Usage:

typescript
import { useProcessImportJob } from '@/lib/queries';

const processJob = useProcessImportJob();
await processJob.mutateAsync(jobId);

See: Data Import Hub Documentation


8. Apply Import Job (Data Import Hub)

Endpoint: POST /functions/v1/apply-import-job

Description: Applies validated import records to target tables (inventory_items, zones, stock_movements, etc.) and creates rollback snapshots.

Request Body:

json
{
  "job_id": "uuid"
}

Response:

json
{
  "success": true,
  "imported_count": 95,
  "message": "Successfully imported 95 records"
}

Usage:

typescript
import { useApplyImportJob } from '@/lib/queries';

const applyImport = useApplyImportJob();
await applyImport.mutateAsync(jobId);

See: Data Import Hub Documentation


9. Rollback Import Job (Data Import Hub)

Endpoint: POST /functions/v1/rollback-import-job

Description: Rolls back an import job, restoring all imported data to its previous state. Admin-only access.

Request Body:

json
{
  "job_id": "uuid"
}

Response:

json
{
  "success": true,
  "rolled_back": 95,
  "message": "Rolled back 95 records"
}

Usage:

typescript
import { useRollbackImportJob } from '@/lib/queries';

const rollback = useRollbackImportJob();
await rollback.mutateAsync(jobId);

See: Data Import Hub Documentation


10. Safety Engagement Metrics

Endpoint: GET /functions/v1/safety-engagement

Description: Aggregates safety engagement data into a single response.

Aggregates:

  • User safety score
  • Total points
  • Recent points log
  • Earned badges
  • Team scores (admin only)
  • Zone scores (admin only)

Caching: 60 seconds TTL per tenant

Response:

json
{
  "tenant_id": "uuid",
  "user_id": "uuid",
  "timestamp": "2025-02-03T10:00:00Z",
  "user_score": {
    "score": 85,
    "period_start": "2025-01-01",
    "period_end": "2025-01-31"
  },
  "total_points": 1250,
  "recent_points": [...],
  "badges": [...],
  "team_scores": [...],  // Admin only
  "zone_scores": [...]   // Admin only
}

Usage:

typescript
import { fetchSafetyEngagement } from "@/lib/api-edge";

const data = await fetchSafetyEngagement();

Fallback: Falls back to direct Supabase queries for scores, points, and badges.


Caching

All Edge Functions use in-memory caching with:

  • Per-tenant isolation: Cache keys include tenant_id
  • Short TTL: 30-60 seconds to balance freshness and performance
  • Cache headers: Responses include X-Cache: HIT or X-Cache: MISS

Cache Invalidation:

  • Automatic expiration after TTL
  • Manual invalidation via cache cleanup (every 5 minutes)
  • Per-tenant cache clearing available

Authentication

All endpoints require:

  • Authorization header: Bearer <access_token>
  • apikey header: Supabase anon key (optional but recommended)

Example:

typescript
const headers = {
  Authorization: `Bearer ${session.access_token}`,
  "Content-Type": "application/json",
  apikey: SUPABASE_ANON_KEY,
};

Error Handling

All Edge Functions:

  1. Try Edge Function first (aggregated, cached)
  2. Fall back to direct queries if Edge Function fails
  3. Maintain backward compatibility with existing code

Error Response:

json
{
  "error": "Error message",
  "details": "Additional error details"
}

Status Codes:

  • 200: Success
  • 401: Unauthorized (missing/invalid token)
  • 403: Forbidden (user not associated with tenant)
  • 405: Method not allowed
  • 500: Internal server error

Performance Benefits

Request Volume Reduction

Before (Direct Queries):

  • Admin Dashboard: 5+ separate REST requests
  • Safety KPIs: 4+ separate REST requests
  • Safety Engagement: 4+ separate REST requests
  • Predictive Scores: 10+ separate REST requests (per entity type)

After (Edge Functions):

  • Admin Dashboard: 1 aggregated request
  • Safety KPIs: 1 aggregated request
  • Safety Engagement: 1 aggregated request
  • Predictive Scores: 1 aggregated request (all entities)

Estimated Reduction: 60-80% fewer REST requests for dashboard views.

Response Time

  • Cached responses: < 50ms (cache hit)
  • Uncached responses: 200-500ms (aggregated queries)
  • Direct queries (fallback): 500-1500ms (multiple sequential requests)

Implementation Notes

Frontend Integration

Edge Functions are integrated as optional optimizations:

  • Frontend tries Edge Function first
  • Falls back to direct Supabase queries if Edge Function fails
  • No breaking changes to existing code

Example Pattern:

typescript
try {
  // Try Edge Function (aggregated, cached)
  const data = await fetchAdminDashboardSummary();
  return data;
} catch (error) {
  // Fallback to direct queries
  console.warn("Edge Function unavailable, using direct queries");
  // ... existing query logic ...
}

Backend Implementation

Edge Functions:

  • Use adminClient (service role) for database access
  • Respect RLS policies via tenant filtering
  • Aggregate queries in parallel using Promise.all()
  • Return structured JSON responses
  • Include cache headers for monitoring

Monitoring

Cache Hit Rate:

  • Check X-Cache header in responses
  • Monitor cache hit/miss ratio
  • Adjust TTL if needed

Performance:

  • Monitor response times
  • Track error rates
  • Compare Edge Function vs. direct query performance

Future Enhancements

Potential improvements:

  • Redis-based distributed caching
  • Longer TTL for less-frequently-changing data
  • Cache warming for common queries
  • Metrics endpoint for cache statistics
  • Webhook-based cache invalidation

References

Released under Commercial License