From 0.5 to 0.6
What to change when upgrading to better-supabase 0.6, which renames kits to blocks, moves the feature modules under better-supabase/blocks, spells out organization and replaces the PermDock settings with authorization providers.
0.6 renames kits to blocks and moves every feature module under
better-supabase/blocks/<name>. Names that said org now say organization,
in TypeScript, in SQL and in event types. The PermDock settings become one
neutral authorization provider.
This release is a clean break: the
old names have no aliases or SQL wrappers, so update imports, config and SQL in
one change.
Update imports
| 0.5 | 0.6 |
|---|---|
createOrgs from better-supabase/orgs | createOrganizations from better-supabase/blocks/organizations |
better-supabase/jobs (queue, cron, idempotency, inbox) | better-supabase/blocks/jobs |
createOutbox, outboxCloudEvent from better-supabase/jobs | better-supabase/blocks/outbox |
purgeAuditLog, PurgeAuditLogOptions from better-supabase/jobs | better-supabase/blocks/audit |
createInbox and the Inbox* types from better-supabase/jobs | createWebhookInbox and WebhookInbox* from blocks/jobs |
entitlementMembers, ENTITLEMENTS_UPDATED from jobs | better-supabase/blocks/entitlements |
hasEntitlement, EntitlementKey from server, next, ssr or react | better-supabase/blocks/entitlements |
better-supabase/notifications | better-supabase/blocks/notifications |
useNotifications from better-supabase/react | better-supabase/blocks/notifications/react |
better-supabase/webhooks | better-supabase/blocks/webhooks |
orgLogoBucket | organizationLogoBucket |
Org names become Organization names, Kit names that describe a feature
become Block names, and Kit names that describe a SQL module become
Module names. The rest of better-supabase/orgs, /notifications and
/webhooks keeps its names under the new subpath (Invitation,
SwitchResult, createWebhooks, signWebhook and so on). Every export that
0.6 renames or removes:
| Subpath | 0.5.1 | 0.6 |
|---|---|---|
. | KitEvent, KitEventMap, KitEventType | BlockEvent, BlockEventMap, BlockEventType |
events | onKitEvent, forwardKitEvents, ForwardKitOptions | onBlockEvent, forwardBlockEvents, ForwardBlockOptions |
events | KIT_ATTRIBUTES, kitCloudEvent, kitEventAttributes | BLOCK_ATTRIBUTES, blockCloudEvent, blockEventAttributes |
events | KitEventMeta, KitEventPattern, KitEventsMatching | BlockEventMeta, BlockEventPattern, BlockEventsMatching |
events | OrgEventData | OrganizationEventData |
otel | traceKitEvents | traceBlockEvents |
orgs | createOrgs, Orgs, OrgsOptions, CreateOrgOptions, OrgAttributes | createOrganizations, Organizations, OrganizationsOptions, CreateOrganizationOptions, OrganizationAttributes |
orgs, notifications, webhooks | KitTransport | BlockTransport |
storage | orgLogoBucket, OrgLogoBucketOptions | organizationLogoBucket, OrganizationLogoBucketOptions |
config | KitsConfig, KitModuleConfig, KitMode, AccessKitConfig | ModulesConfig, ModuleConfig, ModuleMode, AccessModuleConfig |
sql | renderKit, kitLayout, kitPermissionKeys, kitDeprecations | renderModules, moduleLayout, modulePermissionKeys, moduleDeprecations |
sql | kitFilePaths, kitFileVersion, sameKitFile | moduleFilePaths, moduleFileVersion, sameModuleFile |
sql | kitIdType, isKitIdType, KIT_ID_TYPES, KitIdType | moduleIdType, isModuleIdType, MODULE_ID_TYPES, ModuleIdType |
sql | KitContext, KitLayout, KitFile, KitNames, KitTableSpec | ModuleContext, ModuleLayout, ModuleFile, ModuleNames, ModuleTableSpec |
sql | KitContractFunction, KitDeprecation, KitPermissionKey | ModuleContractFunction, ModuleDeprecation, ModulePermissionKey |
sql | KitUpgrade, KitUpgradePlan, InstalledKitModule | ModuleUpgrade, ModuleUpgradePlan, InstalledModule |
sql | KitAccessPermdock, KitPermdock | ModuleAccessProvider, ModuleEntitlementsProvider |
sql, storage, realtime | PermdockCatalog | AuthorizationProvider from better-supabase/config |
storage, realtime | PermdockBucketPolicy, PermdockTopicPolicy | AccessBucketPolicy, AccessTopicPolicy |
config | PermdockPathsConfig | removed; see Move to an authorization provider |
sql | PERMDOCK_SCHEMA, permdockKeys, permdockKeyStatus, PermdockKeyStatus | removed; the provider lists its permissions |
list, storage, realtime and events keep their subpaths.
Update the config
sql.kit (the modules to keep in sync) and the kits key (their settings)
merge into one key, sql.modules. It takes an object keyed by module name,
where every key is a module to keep in sync and its value is that module's
settings. A list of names still works when no module needs settings.
export default defineConfig({
sql: {
modules: {
access: { roles: ["owner", "admin", "member"] },
organizations: {},
invitations: {},
},
},
});Update event types
The organization events are now organization.created,
organization.updated, organization.deleted, organization.role_changed,
organization.member_added, organization.member_removed,
organization.member_left, organization.ownership_transferred and
organization.switched. Update sb.on patterns, outbox consumers and
webhook subscriptions that name an org.* type.
Every event type is now <entity>.<past_tense_verb>, with a snake_case
entity and no third segment. These types were renamed:
| 0.5 | 0.6 |
|---|---|
org.* | organization.* |
ai_chat.message.completed | ai_chat_message.completed |
inbox.conversation.* | inbox_conversation.* |
inbox.message.received | inbox_message.received |
incoming_webhook.rotated | incoming_webhook.token_rotated |
workflow.alert | workflow_alert.triggered |
workflow.run.completed, .failed, .cancelled | workflow_run.completed, .failed, .cancelled |
data_export.ready | data_export.completed |
BLOCK_EVENT_RENAMES from better-supabase/events maps each old
organization type to its new name, for a consumer that reads events written
before the upgrade. The full list of types and their data keys is on the
events page.
Payloads and subjects changed too:
support.startedandsupport.endedcarry a camelCase payload (sessionId,adminId,targetUserId,reason,readOnly,expiresAt,organizationId, andendedByonsupport.ended), and their subject issupport-sessions/<id>.- Subjects are kebab-case plurals:
push-devices/<id>,waitlist-entries/<id>andaudit-entries/<id>. scim.user_*andscim.group_*name the SCIM resourcescimUserIdorscimGroupIdinstead ofid.support.*,notification.created,webhook.disabledand the workflow events carryorganizationId.notification.created,webhook.disabled,attachment.scanned,inbox_message.received,data_export.completed,data_export.failedandorganization.purgedhave an idempotency key, so a retried call writes one event.
Regenerate the SQL modules
The module files now start with -- @bs-module (-- @bs-module-data for the
data files), and the module registry table better_supabase.kit_modules is
now better_supabase.modules. These SQL functions were renamed:
| 0.5 | 0.6 |
|---|---|
member_org_ids(roles) | member_organization_ids(roles) |
has_org_role(org, roles) | has_organization_role(organization, roles) |
org_member_role(org, member) | organization_member_role(organization, member) |
org parameters of the organization functions | organization |
Update your policies and RPCs that call them, then rewrite the module files and create the migrations:
better-supabase sql sync
supabase db schema declarative sync
better-supabase sql dataThe schema diff drops kit_modules and the old functions and creates the new
ones, and sql data writes the modules rows into a migration after it.
A function whose parameter was renamed can't be replaced in place, so apply
the files through the diff rather than by running them on the database.
Move to an authorization provider
better-supabase no longer reads permdock.config.ts, permdock.manifest.json
or permissions.catalog.json. The authorization key takes a provider
object instead, and PermDock builds it from those files with
authorizationProvider({ manifest, catalog }) from permdock/better-supabase:
import { readFileSync } from "node:fs";
import { defineConfig } from "better-supabase/config";
import { authorizationProvider } from "permdock/better-supabase";
const read = (file: string) =>
readFileSync(new URL(file, import.meta.url), "utf8");
export default defineConfig({
authorization: authorizationProvider({
manifest: read("permdock.manifest.json"),
catalog: read("permissions.catalog.json"),
}),
sql: { modules: { access: { model: "provider" } } },
});| 0.5 | 0.6 |
|---|---|
permdock: { manifest, catalog } in the config | authorization: authorizationProvider({ manifest, catalog }) |
sql.modules.access.model: "permdock" | sql.modules.access.model: "provider" |
sql.modules.access.permdock.schema, .scope and .forUser | the provider's functions, tenantScope and the idsWithFor and isPlatformFor templates |
entitlements.permdock: { scope } | the provider's tenantScope |
entitlements.permdock: false | entitlements.memberships: "tenant" |
policy: { permdock: { read, write }, scope, schema } on a bucket | policy: { access: { read, write }, scope, sql }, with bucketPolicy() from permdock/better-supabase |
permdock: { receive, send } on a topic | access: { receive, send, scope, sql }, with topicPolicy() from permdock/better-supabase |
catalog on defineBucket and defineTopic | the provider's permissions (sqlComplete), checked by gen and doctor BS214 |
permdockVerifier from better-supabase/blocks/api-keys | apiKeyVerifier from permdock/better-supabase |
permdock on apiKeyResolver and apiKeyClaims | claim, or apiKeyClaimOptions(manifest) from permdock/better-supabase |
options.scopes: "catalog" on the api-keys module | unchanged; it reads the provider's permissions |
PermdockBucketPolicy, PermdockTopicPolicy, PermdockCatalog | AccessBucketPolicy, AccessTopicPolicy, AuthorizationProvider |
KitAccessPermdock, KitPermdock | ModuleAccessProvider, ModuleEntitlementsProvider |
PERMDOCK_SCHEMA, permdockKeys, permdockKeyStatus | removed; the provider lists its permissions |
The access policies keep the key lists they had. Without sql, they call the
access contract (tenant_ids_with, is_platform), so a bucket or topic on
the tenant scope works under every access model. Doctor's BS107, BS213,
BS214, BS324, BS404, BS405, BS407 to BS409 and BS411 keep their codes and
read the provider; their titles no longer name PermDock.
Run better-supabase sql sync and better-supabase gen after the change. The
rendered SQL calls the same functions as before when the provider builds the
same templates.
better-supabase sql add tenant refuses to write the module when the
provider's access token hook already writes the memberships claim, since the
two hooks would disagree about it. Use the provider's hook, or pass --force
to write the module anyway.
Removed deprecations
The aliases that 0.5 kept for one minor are gone:
| Removed | Use |
|---|---|
A support token whose act has session_id but no kind | tokens minted by 0.5.1 or later (act.kind: "support"); older ones are refused as invalid-chain |
The read-only better_supabase.audit_log view | better_supabase.audit_events (occurred_at, organization_id); the audit module's version 5 step drops the view |
maxUrlLength on defineSupabase and postgrestExecutor | urlLengthLimit |
scopes on the MCP server options | advertisedScopes |
Other breaking changes
- A bucket definition's
path(values)and a connected bucket'spublicUrl()return aResultinstead of throwing aDbException. Read.data(nullon an error) or check.ok. db.$rpcreturns table rows (returns setof customers) andreturns table (...)records in the configured casing, with codecs applied. Remove a snake-to-camel mapping of the result, or pass{ raw: true }to keep database names.gentypes function results as nullable: a scalar result, each element of asetofscalar, a single row and eachreturns tablecolumn are| null. Setfunctions.<name>.notNullinbetter-supabase.config.tswhen a result is never null.notifications.markRead,markUnread,dismissandresolvereturn{ count, items }instead of a number.notifications.send()returns{ id, recipients }, ornullwhen nobody was left.notifications.listpages withcursorinstead ofbefore. It takes the same values: the last item of the previous page, or an instant.- The notifications, organizations and outgoing webhooks blocks take
mappersinstead oferrorMappers. - The leaf entry that runs after
withSupabaseis nowwithBetterDb(betterSupabase).withBetterSupabase(server)is one entry that resolves the caller itself, so you can dropwithSupabase. - The webhook inbox is
createWebhookInboxfrombetter-supabase/blocks/jobs;createInboxfrombetter-supabase/blocks/inboxis the conversation inbox. - The server's
auth.kindgains"apiKey", andDbErrorgains the kindsquota_exceeded(HTTP 429),max_affected(400) andunsupported(501). An exhaustiveswitchover either needs the new cases. deleteManyandupdateManyrefuse awherethat filters nothing. PassallowAll: truetoupdateManyto update every row. An update whosedatasets no columns returnsinvalid_requestinstead ofnot_found.- A
*in alikeorilikepattern is a literal character; use%. - List queries count with
count: "planned"by default. Passcount: "exact"where you show an exact total. - Cursors record the table and sort, so a cursor stored before 0.6 or an
array from
encodeCursor()fails withInvalid cursor. Start again withafter: null. createEdge'scorsoption runswithCorsfrom@supabase/middleware/cors: a preflight needsAccess-Control-Request-Method, and an allow-list addsVary: Origin.gennames colliding_by_relations by every key column (customerByCustomerOrganization) instead of ending them in_, and view copies are no longer relations. Keep an old name withtables.<table>.relations.@supabase/postgrest-typegenis an optional peer. Install it forgen,introspectanddoctorwithpnpm add -D @supabase/postgrest-typegen@0.4.0. AGeneratorInputbuilt by hand needs amodel.Invitationhas a requiredinviterfield (nullwithout the profiles module).better-supabase/nextneeds Next.js 16.3 or later (nextpeer>=16.3 <17). Upgrade Next.js first.better-supabasedepends on@supabase/server^1.9.1; the framework bridges replace its framework adapters.
SQL behaviour changes
These apply once better-supabase sql sync rewrites the module files:
- A table in
exposeloses privileges it doesn't list, includingtruncate,referencesandtrigger. - Idempotency keys have a holder:
begin_idempotentreturnsholder, andcomplete_idempotentandrelease_idempotenttake it. - An invitation past its expiry fails with
INVITATION_EXPIRED(accept) and can no longer be edited withupdate_invitation; callresend_invitationfirst. roleThrough.whereandplatformRoles.through.wherehold for direct writes too, throughbs_role_scopetriggers.mark_notifications_readand the other notification state functions return the changed notifications with the count.inboxno longer installsjobsandstreams,workflowsno longer installsjobs, andai-cacheno longer installstenant. Keep them insql.moduleswhen you use them: withoutjobs, the inbox queues no bot or delivery jobs.webhooks-ininstallsupdated-at.support.endedcarries the session's tenant in its audit entry and outbox event.- Module actions write their audit entries under the shared categories
(
membership,access,security,configuration,billing,data,ai,integration; see Audit). Organization entries wereorganization, support entriessupportand revealed detailsaudit. An adopted log with a category check maps the new names withsql.modules.audit.options.values.category.options.auditCategoryis deprecated. - Settings, flags, billing customers, credentials, connectors, agents,
provider keys, tool policies and approvals, chat shares, incoming webhooks,
webhook secrets, announcements, published workflows and revealed audit
details now write an audit entry and an outbox event, as do the access
catalog's tables (audit entry only). Set
audit: falseon a module to leave it out of the log. support.startedcarries the session's tenant in its audit entry and outbox event.sql.modules.jobs.schemais an error. Remove it: the jobs functions were always inbetter_supabase.
Run the codemod
better-supabase codemod 0.6 renames the Kit and Org exports from the
rename table where your code imports them, maxUrlLength to
urlLengthLimit, and string literals that are exactly a renamed event type
("org.member_added", "org.*") to the new name. It lists the lines it can't rewrite for review:
$rpc calls, publicUrl() and path() calls, and imports from the removed
subpaths. It does not move an import to its new subpath, rewrite SQL or
change config keys. Run it after better-supabase gen, then fix what the
type checker reports.
Last updated on