From 0.53 to 0.54
What to check when upgrading from better-supabase 0.53 to 0.54. Doctor BS315 is an error, tests.authenticate_as looks users up through security definer helpers, withContext takes an async factory, and the tasks module gains set forms. Notes on db.$decode and single-row misses (PGRST116).
0.54.0 collects the CentraKit gaps. Two changes can fail an existing project:
doctor reports BS315 as an error, and tests.authenticate_as(email) now
needs a regenerated pgTAP helper file. Run better-supabase upgrade, then
better-supabase db diff for the new tasks functions.
| Section | Needs code |
|---|---|
| BS315 is an error | Audit the listed tables or exempt them with a reason |
| pgTAP helpers | Run gen again and commit the new test helper |
| Tasks set forms | Run upgrade, then db diff |
Reading rows with $decode | None; read the notes |
| Single-row misses | None; read the notes |
BS315 is an error
Doctor now fails on a table in schemas that has no audit trigger when the
audit module is installed, and on an exempt entry that is a bare glob
without a reason. In 0.53 both were warnings. To clear it:
- Run
better-supabase doctorand note eachBS315target. - For a table whose changes belong in the audit log, add
select better_supabase.audit('public.customers');to its schema file, then runbetter-supabase db diff. - For a table that needs no audit entry, list it in
blocks.audit.options.exemptas{ table: "public.*_archive", reason: "read-only copies" }. - Turn each bare glob string in
exemptinto a{ table, reason }entry. - Run
doctoragain and confirm that noBS315is left.
pgTAP helpers
tests.authenticate_as(email) and the uuid form read auth.users through two
security definer helpers (tests._user_id_by_email and tests._email_of).
A test that is already signed in can now switch to another user without calling
tests.clear_authentication() first. authenticate_as itself stays
security invoker, because Postgres refuses set role inside a
security definer function. Run better-supabase gen and commit the changed
file under supabase/tests.
Tasks set forms
The tasks module is at version 5. archive_tasks(ids uuid[], archived boolean)
and delete_tasks(ids uuid[]) check access once per statement and return
{ ids, skipped }. See archive or remove many items.
The single forms are unchanged.
Reading rows with $decode
db.$decode(table).row(value) and .rows(value) read a row that did not come
from the repository. For each column it reads the configured name first and the
database name second, so a jsonb payload that a SQL function built with
camelCase keys (organizationId) decodes the same as a row with database names
(organization_id). When both keys are present, the configured name wins.
Codecs run on both (int8 text becomes bigint, timestamps become Temporal),
a column the value lacks is left out instead of filled in, and a value that is
not an object returns a validation error in the Result.
Single-row misses
PostgREST answers .single() with PGRST116 (HTTP 406) when the query returns
no row or more than one, and .maybeSingle() turns the no-row case into null.
When better-supabase reads through $client it passes the error through
toDbError, which maps PGRST116 to a not_found DbError. Code that calls
$client yourself and checks error.code === "PGRST116" keeps working as
before. createFakeClient answers .single() and .maybeSingle() the same way,
so a test that expects PGRST116 from a miss behaves like a real server.
Last updated on
Existing apps
Bring better-supabase into an app that already has its own tables, permissions, events and middleware, one module at a time.
From 0.48 to 0.49
What to check when upgrading from better-supabase 0.48 to 0.49. The renames left open by the cleanup finish, notifications gain digests and the statuses held and digested, new blocks declare tenant default seeds, maintenance runs without pg_cron, billing keeps native subscriptions and accounting recognizes revenue over time.