Skip to content

EF: trade-planner-save

A worked exemplar of a Type A (authenticated) write edge function.

At a glance

Nametrade-planner-save
TypeType A (JWT required)
Pathsupabase/functions/trade-planner-save/
Registered inEDGE_FN.SAVE in tiers/users/features/edge-builder/config/edgeBuilderConfig.js
Testssupabase/functions/trade-planner-save/tests/

Purpose

Server-side gateway for all trade_planner_plans mutations. It is an EF (not an RPC) because it writes and enforces ownership/validation with the service role.

Security model

  1. JWT verification via requireAuth() — unauthenticated requests rejected.
  2. user_id is always sourced from the verified JWT, never from the client body.
  3. Plan ownership is verified before any UPDATE.
  4. All incoming fields are validated and sanitised before DB writes.

Trigger

Called from the Trade Planner service via supabase.functions.invoke(EDGE_FN.SAVE, { body }) (wrapped in a TanStack useMutation).

Request

json
{
  "operation": "save_plan",
  "plan_date": "YYYY-MM-DD",
  "plan_type": "daily | weekly | monthly",
  "title": "…",
  "market_bias": "bullish | bearish | sideways | uncertain"
}

operation: 'save_plan' upserts a row (INSERT on first save for a date; UPDATE on subsequent saves for the same date + type). Delete is not supported at any level — plans are permanent; no delete UI is rendered.

Response

ok({ … }) on success; typed errors map to HTTP status via err() (ValidationError → 400, AuthError → 401, ForbiddenError → 403, unhandled → safe 500).

Dependencies

  • _shared: handleCors, requireAuth, ok/err, logger.
  • Table: trade_planner_plans (+ append-only trade_plan_updates). See table index.
  • Complex validation/sanitisation in the function's helpers.ts.
  • Lifecycle counterpart: trade-planner-lifecycle-sweep (Type B cron).
  • Architecture context: TRADE_PLANNER_ECOSYSTEM_PLAN.md.