Appearance
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) | |
|---|---|---|
| Auth | JWT required — requireAuth(req) | Deployed --no-verify-jwt |
| Naming | domain-action (e.g. trade-planner-save) | domain-noun-verb (e.g. trade-planner-lifecycle-sweep) |
| Trigger | Client functions.invoke | Scheduled (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
| File | Exports |
|---|---|
cors.ts | corsHeaders, handleCors(req) — called first in every EF. |
auth.ts | requireAuth, optionalAuth, requireAdmin; typed errors ValidationError (400), AuthError (401), ForbiddenError (403), NotFoundError (404), ConflictError (409), … |
response.ts | ok(data, status?), err(fn, e) — maps typed errors to status, logs unhandled, returns a safe generic 500. |
logger.ts | debug/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 inindex.ts. - Every EF ships co-located
tests/(Deno) covering success, validation, auth/ownership, safe 500s, and webhook signature/idempotency where relevant. Run withnpm run test:ef. - EF names are registered in
EDGE_FNconfig maps — never inline strings. - Full templates + checklist:
documentations/claude/EF_PATTERNS.mdanddocumentations/01-layout/guidelines/supabase-edge-functions-guidelines.md.
See also: Adding an Edge Function · Database.