Skip to content

Backend Architecture

The backend is Supabase: PostgreSQL (with RLS), Postgres RPCs for reads, and 56 Edge Functions (Deno/TypeScript) for writes, secrets, and external HTTP. Full index: Edge Function Index.

Two function types

Type A (user-facing)Type B (cron/internal)
AuthJWT required — requireAuth(req)Deployed --no-verify-jwt
Namingdomain-action (e.g. trade-planner-save)domain-noun-verb (e.g. trade-planner-lifecycle-sweep)
TriggerClient functions.invokeScheduled (cron) / webhook

Shared entrypoint pattern

Every EF follows the same shape (utilities in supabase/functions/_shared/):

ts
import { handleCors } from '../_shared/cors.ts';
import { requireAuth, ValidationError } from '../_shared/auth.ts';
import { ok, err } from '../_shared/response.ts';
import { info, startTimer } from '../_shared/logger.ts';

Deno.serve(async (req) => {
  const preflight = handleCors(req);     // 1. CORS first, always
  if (preflight) return preflight;
  const timer = startTimer();
  try {
    const { user, serviceClient } = await requireAuth(req);  // 2. auth (Type A)
    const body = await req.json();
    if (!body.required) throw new ValidationError('required is required.');  // 3. validate
    // 4. business logic (complex parts in helpers.ts)
    info('fn-name', 'operation.completed', { ms: timer() });
    return ok({ result });               // 5. typed success
  } catch (e) {
    return err('fn-name', e);            // 6. typed error mapping + safe 500
  }
});

_shared utilities

FileExports
cors.tscorsHeaders, handleCors(req) — called first in every EF.
auth.tsrequireAuth, optionalAuth, requireAdmin; typed errors ValidationError (400), AuthError (401), ForbiddenError (403), NotFoundError (404), ConflictError (409), …
response.tsok(data, status?), err(fn, e) — maps typed errors to status, logs unhandled, returns a safe generic 500.
logger.tsdebug/info/warn/error structured JSON (INFO/WARN require an event field), startTimer().

Request-flow examples

Webhooks & idempotency

Webhook-receiver EFs (e.g. finfluencify-stream-webhook) verify an HMAC signature with a timestamp replay window and constant-time comparison, then record every delivery in an audit table with a UNIQUE event id so retries are idempotent. Reuse this pattern for any new webhook receiver.

Scheduled jobs (cron)

Type B EFs run on a schedule (e.g. notifications-fanout every 5 min, daily-scheduled-clean-up, *-lifecycle-sweep). They use the service-role client, batch their work, and enforce a hard timeout below the platform wall-clock limit. pg_cron is enabled; schedules are configured in the Supabase dashboard.

Standards & testing

  • Complex validation/sanitisation lives in helpers.ts, not inline in index.ts.
  • Every EF ships co-located tests/ (Deno) covering success, validation, auth/ownership, safe 500s, and webhook signature/idempotency where relevant. Run with npm run test:ef.
  • EF names are registered in EDGE_FN config maps — never inline strings.
  • Full templates + checklist: documentations/claude/EF_PATTERNS.md and documentations/01-layout/guidelines/supabase-edge-functions-guidelines.md.

See also: Adding an Edge Function · Database.