Appearance
Services Layer
A service is the only client-side place allowed to call supabase.functions.invoke(). Services wrap edge-function calls for a feature and live at tiers/[tier]/features/[name]/services/{feature}Service.js.
Rules
- Only services call
functions.invoke()— never components, hooks, pages, or JSX. - EF names come from an
EDGE_FNconfig map — never inline strings. - Throw on error so callers (usually a TanStack
useMutation) can handle it.
js
// services/featureService.js
import { supabase } from '@/lib/supabaseClient';
import { EDGE_FN } from '../config/featureConfig';
export const featureService = {
async save(payload) {
const { data, error } = await supabase.functions.invoke(EDGE_FN.SAVE, { body: payload });
if (error) throw error;
return data;
},
};EDGE_FN config maps
Every deployed edge function is registered in a per-feature config constant so names are never inlined:
js
// config/edgeBuilderConfig.js (authoritative for Edge Builder)
export const EDGE_FN = {
SAVE: 'trade-planner-save',
UPDATE: 'trade-planner-update',
LIFECYCLE: 'trade-planner-lifecycle',
SHARE: 'trade-planner-share',
};FinFluencify keeps its own map in config/cloudflareConfig.js. The full deployed list is the Edge Function Index.
Mutations
Services are typically consumed through TanStack Query useMutation (the standard for writes), giving loading/error state, optimistic updates, and cache invalidation. Reads, by contrast, use RPC hooks.