name: supabase-cloud-schema-discovery
description: Discover Supabase Cloud database schema when Dashboard is unreachable — enumerate tables via PostgREST error hints, probe columns, and use Edge Functions with service role
category: supabase
tags: [debugging, supabase, postgrest, schema]
Supabase Cloud — Schema Discovery When Dashboard is Unreachable
Problem
You need to discover what tables/columns exist in a Supabase Cloud project, but:
- - Can't access Supabase Dashboard (no login / 2FA)
- - Management API endpoints don't work (wrong paths)
- - Anon key queries return
PGRST205("table not found in schema cache") - -
PGRST205= table not in schema cache (doesn't exist or not exposed) - -
PGRST204= column not found in table schema - -
PGRST202= function/procedure not found - -
42501= permission denied (RLS blocking) - - Schema cache ≠ actual DB schema. PostgREST caches the schema — tables created directly in DB may not appear until cache refreshes (or never if RLS/policies are misconfigured)
- - Anon key + service role key are different. Anon key queries respect RLS; service role bypasses RLS
- - Supabase Dashboard is the best tool — if you can get login access, use it instead of these workarounds
- - BOOT_ERROR on Edge Functions: Use native
fetch()with service role — do NOT import@supabase/supabase-jsfrom esm.sh
Key Insight
PostgREST error hints reveal actual schema names. When PostgREST can't find a table you query, the error response includes a hint field with the closest matching actual table name. By intentionally triggering these errors for guessed table names, you can enumerate what tables really exist.
Technique: Enumerate Tables via Error Hints
`bash
For any table name that doesn't exist, PostgREST returns:
{"code":"PGRST205","hint":"Perhaps you meant the table 'public.actual_name'"}
The hint tells you what IS in the schema!
Try common plural forms and variations
for table in broker_links broker_accounts broker_tools broker_dashboard \
profiles team_members tasks automations projects users; do
result=$(curl -s "https://YOURREF.supabase.co/rest/v1/$table?select=id&limit=1" \
-H "apikey: YOUR_ANON_KEY")
if echo "$result" | grep -q "PGRST205"; then
hint=$(echo "$result" | grep -oP "(?<=table 'public.)[^']+")
echo "$table → $hint"
else
echo "$table → EXISTS"
fi
done
`
Technique: Enumerate Columns of a Known Table
`bash
Try common column names to map a table's schema
for col in id title name description status role type responsible_name \
created_at updated_at created_by user_id email; do
result=$(curl -s "https://YOURREF.supabase.co/rest/v1/known_table?select=$col&limit=1" \
-H "apikey: YOUR_ANON_KEY")
if echo "$result" | grep -q "PGRST204"; then
echo "$col → EXISTS"
else
echo "$col → missing or error"
fi
done
`
Common error codes:
Management API — Correct Endpoints
The Supabase Management API base is https://api.supabase.com/v1/:
`bash
List projects (works)
curl "https://api.supabase.com/v1/projects" \
-H "Authorization: Bearer sbp_YOUR_TOKEN"
These commonly FAIL (wrong paths — verify current docs):
/v1/projects/{ref}/database/tables ← often 404
/v1/projects/{ref}/relations/tables ← often 404
/v1/projects/{ref}/postgrest/config/reload ← often 404
`
When All Else Fails: Edge Function with Service Role
Create a minimal Edge Function using createClient from esm.sh:
`typescript
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)
// Now query anything — service role bypasses RLS and schema cache issues
const { data } = await supabase.from('any_table').select('*')
return new Response(JSON.stringify(data))
`
Caveat: BOOT_ERROR from esm.sh imports are common. If the function fails to start, use native fetch() with service role key via REST instead:
`typescript
const r = await fetch(${supabaseUrl}/rest/v1/any_table?select=*, {
headers: {
"Authorization": Bearer ${Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')},
"apikey": Deno.env.get('SUPABASE_SERVICE_ROLE_KEY'),
}
})
const data = await r.json()
`