Skip to content

feat(supabase): postgrest data-api resolver#141

Open
jaylann wants to merge 1 commit into
stagefrom
feat/supabase-postgrest-resolver
Open

feat(supabase): postgrest data-api resolver#141
jaylann wants to merge 1 commit into
stagefrom
feat/supabase-postgrest-resolver

Conversation

@jaylann

@jaylann jaylann commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds SupabasePostgrestResolver (name="supabase_postgrest") — the tabular complement to the auth (feat: supabase auth resolver (auth.users) #56) and storage (feat: supabase storage resolver #57) resolvers: reaches a subject's PII in PostgREST-exposed application tables (/rest/v1/...), driven by an explicit, auditable list of PostgrestTable/PostgrestColumn declarations (no schema discovery).
  • Generalizes errors.raise_for_taxonomy with a system= label so the shared taxonomy names the rejecting Supabase surface; auth keeps its exact message.
  • Attests an AttestingResolver covered surface built from the declared columns; passes the shared ResolverConformanceSuite; property tests pin no-bleed and idempotent convergence.
  • Closes feat: supabase postgrest resolver #70.

Erasure/export semantics

No change to what any existing resolver deletes or exports — this is purely additive (a new resolver, new ref kind supabase_postgrest). MINOR feat:.

New surface introduced by this resolver:

  • Export (Art. 15): per declared table, GET /rest/v1/{table}?{subject_column}=eq.{id}&select=... → one ExportRecord per populated declared column, sourced under the table name. Null/missing cells are skipped.
  • Erase (Art. 17): per declared table, DELETE /rest/v1/{table}?{subject_column}=eq.{id} with Prefer: return=representation. An empty representation across every declared table ⇒ already_absent=True (PostgREST signals no-match with an empty array, not a 404). The subject id rides httpx params as eq.<id> and is matched literally — a hostile id (/, .., ,, spaces) never widens the filter or targets a sibling stem (pinned by a unit test + property tests).

Adding/recategorising a declared column changes that deployment's export surface; the config list is the auditable record of what the resolver reaches.

Risk

Low. Self-contained new module; no core changes. Empty tables raises ConfigurationError (no degraded mode). Service-role key bypasses RLS — documented server-side-only. Fresh httpx client per call (ADR 0006), no cached loop-bound state. Rollback = revert the commit; nothing else depends on it.

⚠️ CI note: an unrelated, pre-existing failure on stagetest_resurrecting_a_row_deleted_row_flips_verified (a test-helper bug in #128 for composite-FK schemas) — may show red here. Tracked in #140; not caused by this PR (reproduces on stage with none of these changes).

Checklist

Add SupabasePostgrestResolver — the tabular complement to the auth and
storage resolvers (#70). It reaches a subject's PII held in
PostgREST-exposed application tables, driven by an explicit, auditable
list of PostgrestTable/PostgrestColumn declarations (no schema discovery).

Per table, export issues GET /rest/v1/{table}?{subject_column}=eq.{id}
&select=... and emits one ExportRecord per populated declared column;
erasure issues DELETE ... with Prefer: return=representation. An empty
representation across every declared table is already_absent=True
(absence is PostgREST's empty array, not a 404). The subject id rides
httpx params as eq.<id> and is matched literally, so a hostile id never
widens the filter or hits a sibling. Empty tables raise ConfigurationError.

errors.raise_for_taxonomy gains a system= label so the shared taxonomy
names the rejecting Supabase surface (auth keeps its existing message).
The resolver attests an AttestingResolver covered surface built from the
declared columns and passes the shared ResolverConformanceSuite; property
tests pin no-bleed and idempotent convergence. Service-role key bypasses
RLS — server-side only; fresh httpx client per call (ADR 0006).

Signed-off-by: Justin Lanfermann <Justin@Lanfermann.dev>
@jaylann jaylann added type:feat New capability area:resolvers Resolver interface & registry labels Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:resolvers Resolver interface & registry type:feat New capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: supabase postgrest resolver

1 participant