docs(self-hosting): the app calls functions now, not tables - #1000
Conversation
Checked against origin/release/2.2.0 and against sql/schema.sql in the backend repo. Most of the page holds: both env var names, `just build`, the five selectable food sources, the eight translation locales and the machine- translation disclosure all match the code exactly. The query mechanism does not. - **"The app reads exactly two relations" is no longer how it works.** There is not a single `.from(` left in sp_food_data_source.dart -- every read goes through one of five Postgres functions over RPC. That was a privacy change (#882): a PostgREST GET puts the search term in the URL where the API gateway logs it, an RPC carries it in the body. Two of the five, added for #864, read food_portion and food_portion_translation, so the app's required surface is two relations plus five functions over four tables. - **The text-search configurations moved.** `english` and `simple` now live inside the functions, next to the indexes that have to agree with them, rather than being passed by the app. - **Troubleshooting missed the likeliest failure and misattributed another.** A database built from a pre-RPC schema has no search functions, or has them without the execute grants -- schema.sql revokes from public and grants to anon/authenticated explicitly -- so a correctly populated database returns nothing. That is now the first item, with a pointer to sql/migrations/ for self-hosters upgrading in place. Access to food_summary was also attributed to RLS; a materialized view has no RLS, and schema.sql says so, restricting it with a grant instead. Refs #991
There was a problem hiding this comment.
🟡 Changes recommended
The troubleshooting section suggests missing FTS indexes can lead to empty results, but indexes affect performance rather than query correctness, so the doc should be corrected for accuracy.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Updates the Supabase self-hosting documentation to reflect the app’s current RPC-based query mechanism (functions over .rpc(...)) rather than direct PostgREST table queries, aligning the doc with the privacy-driven change from #882 and the newer portion-related RPC reads from #864.
Changes:
- Replaces the “reads two relations directly” description with an RPC-centric overview and a list of the five required Postgres functions.
- Clarifies that
english/simpleFTS configurations now live inside the corresponding search functions (not in the app). - Reworks troubleshooting to prioritize missing RPC functions / missing execute grants, and clarifies view access control (grants vs RLS).
File summaries
| File | Description |
|---|---|
| docs/supabase-self-hosting.md | Updates self-hosting guidance to match the app’s function-over-RPC query surface and corrects troubleshooting around grants/RLS. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| - **The search functions are missing, or `anon` cannot execute them.** This is the first thing to check on any database created before the RPC work, or from a partial schema run. `schema.sql` revokes execute from `public` and grants it to `anon` and `authenticated` explicitly, so a correctly populated database still returns nothing without those grants. An existing self-hosted copy needs the migrations in `sql/migrations/` applied — the search, portion-label and portions functions each arrived in one. | ||
| - **The `food_summary` materialized view hasn't been refreshed** after import (`import_fdc.py` does this at the end of every run). | ||
| - **Grants on the view are missing.** Note this is grants, not RLS: a materialized view has no row-level security, so `schema.sql` restricts it with `grant select on food_summary to anon, authenticated` instead. Base tables do use RLS — public read, `service_role` write. | ||
| - **The full-text-search indexes are missing**, which shows up as slow or empty search rather than an error. |
Copilot: indexes do not affect query correctness, so listing them among the causes of 'no results' pointed a self-hoster at the wrong thing. A sequential scan returns the same rows; what a missing index costs is time, and on a large table a timeout.
There was a problem hiding this comment.
🟢 Approval recommended
Only a minor documentation spelling consistency nit was found; the functional/technical content aligns with the app’s current RPC-based query implementation.
Review details
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
docs/supabase-self-hosting.md:34
- Spelling: the table entry uses British English (“Localised”), but the rest of the repo/codebase consistently uses “localized” (e.g.,
localizedNameinSpFoodDTO). Consider using “Localized” here for consistency.
| `search_food_translation` | Localised name search |
- Files reviewed: 1/1 changed files
- Comments generated: 0 new
- Review effort level: Lite
Last of the FIX verdicts from the
docs/audit. Checked againstorigin/release/2.2.0and againstsql/schema.sqlin the backend repo.What survived: both env var names match
env.dartand.env.example;just buildexists and does what the page says; the five selectable food sources matchSPConst.settingsSelectableFoodSourcesexactly (INDB and TBCA still not selectable, as the page says); the eight translation locales matchSPConst.translationLocaleOf; the machine-translation disclosure still exists; every backend path named exists. The premise is intact — this is still how you self-host.The query mechanism changed and the page didn't
The page says the app "reads exactly two relations from the backend schema" and filters them directly. There is not a single
.from(left insp_food_data_source.dart. Every read goes through one of five Postgres functions over RPC.That was a privacy change (#882): a PostgREST
GETputs the search term in the URL, where the API gateway logs it; an RPC carries it in the body. Two of the five functions were added for #864 and readfood_portionandfood_portion_translation, so the app's required surface is now two relations plus five functions over four tables.The page now lists the five with what each is for. The
englishandsimpletext-search configurations also moved inside the functions, next to the indexes that have to agree with them.Troubleshooting had the wrong first answer
It listed three causes for "no search results". The likeliest one today was absent: a database built from a pre-RPC schema has no search functions, or has them without execute grants —
schema.sqlrevokes frompublicand grants toanon/authenticatedexplicitly, so a correctly populated database still returns nothing. That is now first, with a pointer tosql/migrations/for anyone upgrading in place (six migrations there, three of them the RPC work).It also attributed
food_summaryaccess to RLS. A materialized view has no row-level security —schema.sqlsays so in a comment and restricts it withgrant select on food_summary to anon, authenticatedinstead. Base tables do use RLS, so the distinction now appears explicitly rather than the page being simply wrong.Checked
sp_food_data_source.darton the release branch: zero.from(, six_rpcRows/rpc(call sitesSPConstsql/schema.sql: therevoke execute/grant executeblock and the "Materialized views have no RLS" comment both quoted from the filesql/migrations/contains the three RPC migrations I point self-hosters atThis one has no runtime doc test either — same gap as
export-format.mdin #999.