supabase-validation
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseThis skill is safe for use in any project. It never runs reset/break loops or destructive commands. Those exist only in the supabase-debug-playground teaching harness and are not part of this skill.
本技能可安全用于任何项目。 它绝不会执行重置/中断循环或破坏性命令。 这类命令仅存在于supabase-debug-playground教学工具中,不属于本技能的范畴。
Normative Principle
规范性原则
No Evidence. Not done.
Every action this skill governs — write, deploy, migrate, fix — is incomplete
until observable, raw output confirms it. This is not a style preference; it is
the contract this skill enforces.
Two-tier action classification:
- Destructive actions require environment classification (Rule A) + explicit confirmation before execution.
- High-sensitivity file edits (any file under ) require showing a diff + confirmation before applying — even if the operation is not destructive by the definition below.
supabase/
无证据,不完成。
本技能管控的所有操作——编写、部署、迁移、修复——在得到可观察的原始输出确认前,都不算完成。这并非风格偏好,而是本技能强制执行的规则。
两级操作分类:
- 破坏性操作:需要先进行环境分类(规则A),再获得明确确认后方可执行。
- 高敏感度文件编辑(目录下的任何文件):即使操作本身不属于下述定义的破坏性操作,也需先展示差异内容并获得确认,才能应用更改。
supabase/
Destructive Operations — Definition
破坏性操作的定义
Destructive operations are defined here so the term is not interpreted loosely.
Any rule that gates on "destructive" applies to all items in this list:
- Dropping tables or columns
- Resetting the database ()
supabase db reset - Replaying migrations against any live project (local, staging, or production — even local, unless explicitly confirmed disposable)
- Deleting rows or truncating tables
- Overwriting a deployed edge function
- Modifying or dropping RLS policies
- Any write using key
service_role
If an operation is not on this list, it is still subject to Rules B and A
(evidence and environment classification) but does not require the full
destructive-operation confirmation protocol.
此处明确定义破坏性操作,避免术语被随意解读。所有基于“破坏性”设置限制的规则,均适用于以下所有操作:
- 删除表或列
- 重置数据库()
supabase db reset - 对任何线上项目(本地、预发布或生产环境——即使是本地环境,除非明确确认可随意处置)重放迁移
- 删除行或截断表
- 覆盖已部署的Edge Function
- 修改或删除RLS策略
- 使用密钥执行的任何写入操作
service_role
如果操作不在此列表中,仍需遵守规则B和规则A(证据要求和环境分类),但无需遵循完整的破坏性操作确认流程。
Global Clause — Non-Interactive Mode
全局条款——非交互模式
Treat as non-interactive if any of the following are true:
- No active chat session is available to receive a reply
- Running inside a CI job, automated workflow, or scheduled script
- Agent mode with ,
autoApprove, or equivalent flag set--yes - The agent cannot determine whether a reply is possible
If uncertain, treat as non-interactive.
Rule: If user confirmation is required (by any rule below) but the agent is
running in a non-interactive context, the agent must:
- Output the exact command, query, or SQL that would be executed
- Explain what it does and why confirmation is required
- Not execute it
A rule requiring confirmation does not become optional because the agent cannot
ask. It becomes a manual handoff.
满足以下任一条件时,视为非交互模式:
- 无可用的活跃聊天会话接收回复
- 在CI任务、自动化工作流或定时脚本中运行
- 启用、
autoApprove或等效标志的Agent模式--yes - Agent无法确定是否可发送回复
若存在不确定性,默认视为非交互模式。
规则: 如果任何规则要求用户确认,但Agent处于非交互环境中,则Agent必须:
- 输出将执行的具体命令、查询或SQL语句
- 说明该操作的作用以及需要确认的原因
- 不执行该操作
要求确认的规则不会因为Agent无法询问就变为可选,而是转为手动交接。
Global Clause — Never Disclose Secrets
全局条款——绝不泄露机密
Never print, paste, log, or include in a diff:
SUPABASE_ANON_KEY- (or any
SUPABASE_SERVICE_ROLE_KEYkey)service_role - JWTs or bearer tokens of any kind
- Any value that begins with (base64-encoded JWT)
eyJ - Any secret from ,
.env, or equivalent files.env.local
If the agent needs to confirm a key is present: state "I can see a value is
set for ." If identity confirmation is needed, ask the user
to confirm the last 4 characters — do not print them.
SUPABASE_ANON_KEYShowing is permitted — host portion only (e.g.,
). Do not include query strings or path segments that
may carry tokens.
SUPABASE_URLhttps://xyz.supabase.coThis rule applies even in local development.
绝打印、粘贴、记录或在差异对比中包含以下内容:
SUPABASE_ANON_KEY- (或任何
SUPABASE_SERVICE_ROLE_KEY密钥)service_role - 任何类型的JWT或Bearer令牌
- 任何以开头的值(Base64编码的JWT)
eyJ - 来自、
.env或等效文件的任何机密信息.env.local
如果Agent需要确认密钥是否存在: 说明“我已确认已设置值。”若需要确认身份,请用户确认密钥的最后4位字符——不要打印完整密钥。
SUPABASE_ANON_KEY允许展示——仅展示主机部分(例如)。请勿包含可能携带令牌的查询字符串或路径段。
SUPABASE_URLhttps://xyz.supabase.co本规则即使在本地开发环境中也适用。
Rule A — Environment Classification Gate
规则A——环境分类校验
Before any write, migration, RLS change, or function deploy, the agent must
classify the environment and surface evidence to the user. It must not proceed
until the environment type is confirmed.
Classification levels:
- —
localisSUPABASE_URL, orhttp://localhost:*shows local instancesupabase status - — known non-production remote ref, explicitly confirmed by user
staging - — any
productionURL or unrecognised project ref*.supabase.co
Evidence the agent must display before acting:
- The value of (host portion only — no keys)
SUPABASE_URL - Output of or
supabase statusshowing the project refsupabase projects list - For production: the ref + any branch/context information available
Enforcement:
- — agent may proceed with non-destructive validation commands autonomously. Non-destructive examples:
localschema inspection queries,SELECT,supabase functions logs/ HTTP request replays,curl,git diff.supabase gen types typescript --local - — agent must state the environment and confirm intent before writing
staging - — agent must surface the full command for user review and require explicit "yes, proceed" before executing anything that writes or modifies schema
production - If environment cannot be determined — treat as production; do not execute
No Environment Assumptions (see also Rule G): The agent must never infer
environment type from file presence alone — the existence of ,
, or a directory does not confirm the
environment is local. Environment classification must be based only on runtime
evidence: the value, output of , or an explicit
user statement. Rule G extends this constraint to all tasks, including read-only
inspection — not only pre-action gates.
.envsupabase/config.tomlsupabase/SUPABASE_URLsupabase status在执行任何写入、迁移、RLS更改或函数部署操作前,Agent必须对环境进行分类,并向用户展示相关证据。在环境类型得到确认前,不得继续执行操作。
分类级别:
- ——
local为SUPABASE_URL,或http://localhost:*显示为本地实例supabase status - ——已知的非生产环境远程引用,且已得到用户明确确认
staging - ——任何
production格式的URL,或无法识别的项目引用*.supabase.co
Agent在执行操作前必须展示的证据:
- 的值(仅主机部分——不含密钥)
SUPABASE_URL - 或
supabase status的输出,展示项目引用supabase projects list - 生产环境:项目引用以及可用的分支/上下文信息
执行规则:
- ——Agent可自主执行非破坏性验证命令。非破坏性示例:
local架构查询、SELECT、supabase functions logs/HTTP请求重放、curl、git diff。supabase gen types typescript --local - ——Agent必须说明环境类型,并在执行写入操作前确认用户意图
staging - ——Agent必须展示完整命令供用户审核,并在执行任何写入或修改架构的操作前,要求用户明确回复“yes, proceed”
production - 若无法确定环境类型——视为生产环境,不执行操作
不得假设环境(另见规则G): Agent不得仅根据文件存在情况推断环境类型——、或目录的存在,不能确认环境为本地。环境分类必须仅基于运行时证据:的值、的输出,或用户的明确说明。规则G将此约束扩展至所有任务,包括只读检查——不仅是操作前的校验。
.envsupabase/config.tomlsupabase/SUPABASE_URLsupabase statusRule B — No Silent Writes
规则B——禁止静默写入
An agent must never perform a write operation and report completion without
returning observable evidence that the write succeeded.
Required evidence by operation type:
| Operation | Required evidence |
|---|---|
| Returned row with |
| Row count or returned rows confirming the change |
| RLS policy change | Output of |
| Edge function deploy | HTTP response payload containing |
| Migration | |
| Schema type regen | |
If the operation produces no returnable evidence (e.g., ),
re-run the operation with chained, or run a follow-up read query.
Prefer: return=minimal.select()Surface the raw output. Do not summarise before showing it.
Return the actual payload — row object, JSON body, query result, diff — so the
user can verify directly. You may summarise after showing raw output.
// Bad
"Insert successful."
// Good
{ id: 42, created_at: "2026-02-28T20:14:22Z" }
// (then) "Insert confirmed — row id 42 returned."Agent执行写入操作后,若未返回可观察的成功证据,不得报告任务完成。
各操作类型所需的证据:
| 操作 | 所需证据 |
|---|---|
| 返回包含 |
| 确认更改的行数或返回的行 |
| RLS策略更改 | |
| Edge Function部署 | 包含 |
| 迁移 | |
| Schema类型重新生成 | |
如果操作无法返回可验证的证据(例如使用),则需重新执行操作并链式调用,或运行后续的读取查询。
Prefer: return=minimal.select()展示原始输出,不得先总结。
返回实际的响应内容——行对象、JSON体、查询结果、差异对比——以便用户直接验证。可在展示原始输出后再进行总结。
// 错误示例
"插入成功。"
// 正确示例
{ id: 42, created_at: "2026-02-28T20:14:22Z" }
// (随后) "插入已确认——返回行id为42。"Rule C — service_role
Restricted Use
service_role规则C——service_role
的受限使用
service_roleservice_rolePermitted without user confirmation:
- Read-only diagnostics only — queries to inspect schema, policies, or data for debugging purposes
SELECT
Require explicit user confirmation before use:
- Any write, insert, update, or delete using
service_role - Any operation that modifies schema or policies using
service_role
Confirmation protocol:
- State clearly: "This operation requires which bypasses RLS."
service_role - Display the environment classification (Rule A) as part of the request
- Show the exact command or query that will be run
- Wait for explicit "yes" — do not infer consent from prior approval of a related step
- If non-interactive: output the command for manual review, do not execute
service_role无需用户确认即可使用的场景:
- 仅用于只读诊断——查询,用于检查架构、策略或数据以进行调试
SELECT
使用前需获得用户明确确认的场景:
- 使用执行的任何写入、插入、更新或删除操作
service_role - 使用修改架构或策略的任何操作
service_role
确认流程:
- 明确说明:“此操作需要使用,它会绕过RLS策略。”
service_role - 将环境分类结果(规则A)作为请求的一部分展示
- 展示将执行的具体命令或查询
- 等待用户明确回复“yes”——不得从用户对相关步骤的先前批准中推断同意
- 若处于非交互模式:输出命令供手动审核,不执行操作
Rule D — Safe Fallback When Tooling Is Unavailable
规则D——工具不可用时的安全回退
The agent must not hallucinate access it doesn't have.
If a required tool is unavailable:
- State the missing capability explicitly
- Propose a non-destructive alternative:
- Instead of CLI logs → capture ,
error.code,error.messagefrom the client SDK responseerror.hint - Instead of → ask user to run it and paste the output
supabase status - Instead of DB introspection → provide the exact SQL for the user to run in the dashboard SQL editor
- Instead of CLI logs → capture
- Never invent log output, query results, or command output
- Never report a validation as passed unless the agent itself received and parsed the response
Prohibited:
- "I checked the logs and there are no errors" without having received log output
- "The migration ran successfully" without confirmed evidence
- "The policy is in place" without output showing it
pg_policies
Agent不得虚构自身不具备的访问权限。
若所需工具不可用:
- 明确说明缺失的功能
- 提出非破坏性的替代方案:
- 无法使用CLI日志时 → 从客户端SDK响应中捕获、
error.code、error.messageerror.hint - 无法使用时 → 请用户运行该命令并粘贴输出结果
supabase status - 无法进行数据库自省 → 提供精确的SQL语句,让用户在控制台的SQL编辑器中运行
- 无法使用CLI日志时 → 从客户端SDK响应中捕获
- 不得编造日志输出、查询结果或命令输出
- 除非Agent自身已接收并解析响应,否则不得报告验证通过
禁止行为:
- 未接收日志输出就声称“我检查了日志,无错误”
- 无确认证据就声称“迁移已成功运行”
- 无输出就声称“策略已生效”
pg_policies
Rule E — Minimal File Mutations
规则E——最小化文件变更
Required behaviour:
- Prefer the smallest diff that achieves the fix
- Before applying any file change, show a diff or describe line-level changes
- Never use a "replace entire file" pattern unless the user explicitly requests it
- For Supabase-specific files (,
supabase/functions/**,supabase/migrations/**): treat all changes as high-sensitivity and show the diff before applyingsupabase/types.gen.ts
Why Supabase files are high-sensitivity:
- Overwriting a migration file that has already run creates drift — it does not undo the migration
- Overwriting with stale content breaks TypeScript without a compile error
types.gen.ts - Overwriting an edge function's with an earlier version silently reverts a fix
index.ts
Permitted without confirmation: adding new files where no existing file is
displaced.
Requires confirmation: modifying or deleting any existing file under
.
supabase/必须遵守的行为:
- 优先采用能修复问题的最小差异更改
- 在应用任何文件更改前,展示差异内容或描述行级别的更改
- 除非用户明确要求,否则不得使用“替换整个文件”的方式
- 对于Supabase特定文件(、
supabase/functions/**、supabase/migrations/**):所有更改均视为高敏感度操作,应用前需展示差异内容supabase/types.gen.ts
为何Supabase文件属于高敏感度:
- 覆盖已执行的迁移文件会导致数据漂移——无法撤销已执行的迁移
- 用过期内容覆盖会在无编译错误的情况下破坏TypeScript代码
types.gen.ts - 用旧版本覆盖Edge Function的会静默回滚修复内容
index.ts
无需确认即可执行的操作: 添加新文件,且不替换现有文件。
需要确认的操作: 修改或删除目录下的任何现有文件。
supabase/Rule G — No Hidden Context Assumption
规则G——禁止假设隐藏上下文
The agent must not infer Supabase project configuration from local file presence
alone. The existence of , , or a
directory does not confirm which project is active, whether it is local or
remote, or whether the configuration applies to the current task.
.envsupabase/config.tomlsupabase/Environment classification must be based only on runtime evidence:
- The resolved value of (from the active shell environment — not a
SUPABASE_URLfile the agent reads in isolation).env - Output of or
supabase statussupabase projects list - An explicit user statement (e.g., "I'm working against our staging project")
What this prevents:
- A skill applied inside a monorepo where a folder exists but belongs to a different sub-project
supabase/ - An agent assuming because it sees
localin ahttp://localhostfile that may be stale or inactive.env - Hallucinated project context: "this looks like a local project" based on file presence alone
Required agent phrasing: "I can see a directory and a
file. To classify the environment I need to confirm the active .
Running …" — not "This looks like a local project."
supabase/.envSUPABASE_URLsupabase statusRelationship to Rule A: Rule G governs what the agent may assume at any
point, including read-only tasks. Rule A governs what the agent must confirm
before acting. Rule G is the precondition; Rule A is the gate.
Agent不得仅根据本地文件存在情况推断Supabase项目配置。、或目录的存在,无法确认当前激活的项目、环境是本地还是远程,也无法确认配置是否适用于当前任务。
.envsupabase/config.tomlsupabase/环境分类必须仅基于运行时证据:
- 的解析值(来自活跃的Shell环境——而非Agent单独读取的
SUPABASE_URL文件).env - 或
supabase status的输出supabase projects list - 用户的明确说明(例如“我正在处理我们的预发布项目”)
此规则可避免的问题:
- 在单体仓库中应用技能时,文件夹属于其他子项目
supabase/ - Agent因在文件中看到
.env就假设是本地环境,但该文件可能已过期或未激活http://localhost - 基于文件存在情况就虚构项目上下文:“这看起来是一个本地项目”
Agent必须使用的表述: “我已检测到目录和文件。为了分类环境,我需要确认当前激活的。正在运行……”——而非“这看起来是一个本地项目。”
supabase/.envSUPABASE_URLsupabase status与规则A的关系: 规则G管控Agent在任何时候可假设的内容,包括只读任务。规则A管控Agent在执行操作前必须确认的内容。规则G是前提条件,规则A是执行门槛。
Activation Rule — When This Skill Applies
激活规则——本技能的适用场景
This skill MUST activate automatically whenever any of the following signals
appear in code, config, or commands:
- is imported or used
supabase-js - is called
supabase.rpc() - ,
.from(...).insert(), or.update()is used.delete() - ,
CREATE FUNCTION, orCREATE POLICYappears in SQLALTER TABLE - is run
supabase functions deploy - is relevant to the task
supabase gen types - Any migration touches schema or RLS
- Any file under is modified
supabase/functions/ - Any file under or
supabase/migrations/is modifiedsupabase/seed.sql - Any command is run (push/reset/diff)
supabase db *
When activated:
- Add a validation step to the task plan before execution begins
- Execute that validation step before reporting completion
- Do not ask the user whether to validate — validate automatically
- If execution is not possible, state that explicitly and provide the exact commands for the user to run
当代码、配置或命令中出现以下任一信号时,本技能必须自动激活:
- 导入或使用
supabase-js - 调用
supabase.rpc() - 使用、
.from(...).insert()或.update().delete() - SQL中出现、
CREATE FUNCTION或CREATE POLICYALTER TABLE - 执行命令
supabase functions deploy - 与当前任务相关
supabase gen types - 任何迁移操作涉及Schema或RLS
- 修改目录下的任何文件
supabase/functions/ - 修改或
supabase/migrations/目录下的任何文件supabase/seed.sql - 执行任何命令(push/reset/diff)
supabase db *
激活后的行为:
- 在任务计划中添加验证步骤,再执行操作
- 在报告完成前执行该验证步骤
- 无需询问用户是否验证——自动执行验证
- 若无法执行操作,需明确说明并提供用户可运行的具体命令
Validation Contract
验证契约
A Supabase task is not complete when code compiles or a command exits 0.
It is complete only when the validation step passes.
Definition of done:
- Failure or baseline confirmed (run the current behaviour first)
- Fix applied
- Validation executed
- Validation produces a binary pass result
Required plan shape:
- Run current behaviour — confirm failure if fixing, baseline if building
- Diagnose — read the actual error (code, message, hint)
- Apply minimal fix
- Verify — binary pass/fail, not eyeballing
- Report completion
Hard gates — none of these are optional:
- "Looks correct" is not a valid success signal
- "Exit code 0" alone is not a valid success signal
- Never skip verification because the code looks right — run the check
- If a verify step fails, diagnose before retrying — do not loop without reading the error
Supabase任务的完成标准并非代码编译成功或命令退出码为0。只有当验证步骤通过时,任务才算完成。
完成定义:
- 确认失败或基准(先运行当前行为)
- 应用修复
- 执行验证
- 验证产生明确的通过/失败结果
必须遵循的计划流程:
- 运行当前行为——修复场景下确认失败,构建场景下确认基准
- 诊断——读取实际错误(错误码、消息、提示)
- 应用最小化修复
- 验证——明确的通过/失败,而非主观判断
- 报告完成
硬性要求——以下均为必选项:
- “看起来正确”不是有效的成功信号
- “退出码0”单独不能作为有效的成功信号
- 绝不能因为代码看起来正确就跳过验证——必须运行检查
- 若验证步骤失败,需先诊断再重试——不得在未读取错误的情况下循环重试
Pattern Index
模式索引
| Pattern | Trigger | Passes when |
|---|---|---|
| 1 — Edge function (local) | Change to a function running via | HTTP 200 + |
| 2 — Edge function (production) | | HTTP 200 + |
| 3 — RPC | | No error + response contains expected fields |
| 4 — CRUD / Insert | Any | Non-null array with |
| 5 — RLS | | All 3 roles pass: unauthed blocked + authed allowed + service_role allowed |
| 6 — Schema migration | Migration adding, removing, or renaming a column | |
| 模式 | 触发条件 | 验证通过条件 |
|---|---|---|
| 1 — Edge Function(本地) | 修改通过 | HTTP 200 + |
| 2 — Edge Function(生产) | 向真实项目执行 | HTTP 200 + |
| 3 — RPC | | 无错误 + 响应包含预期字段 |
| 4 — CRUD / 插入 | 通过supabase-js执行的任何插入、更新或删除操作 | 返回非空数组且包含带 |
| 5 — RLS | | 三种角色验证均通过:未认证用户被拦截 + 已认证用户被允许 + service_role被允许 |
| 6 — Schema迁移 | 添加、删除或重命名列的迁移 | |
Pattern 1 — Edge Function (Local)
模式1——Edge Function(本地)
Trigger: after any change to a Supabase edge function running locally via
.
supabase functions serveValidation steps:
- POST to
http://localhost:54321/functions/v1/<name> - Assert HTTP 200
- Assert response body is valid JSON with
ok: true - Assert is present in the response body
request_id
Fail signal: HTTP 500, empty body, no , or
returning .
request_idDeno.env.get(...)undefinedDiagnostic: errors stream to the terminal —
read them directly. There is no command for local dev.
supabase functions servesupabase functions logsDocs:
Do not report done until: HTTP 200 + + confirmed.
ok: truerequest_id触发条件: 修改通过在本地运行的Edge Function后。
supabase functions serve验证步骤:
- 向发送POST请求
http://localhost:54321/functions/v1/<name> - 断言HTTP状态码为200
- 断言响应体为有效的JSON且包含
ok: true - 断言响应体中存在
request_id
失败信号: HTTP 500、空响应体、无,或返回。
request_idDeno.env.get(...)undefined诊断方式: 错误会输出到的终端——直接读取这些错误。本地开发环境无命令。
supabase functions servesupabase functions logs文档:
在满足以下条件前,不得报告完成: 确认HTTP 200 + + 存在。
ok: truerequest_idPattern 2 — Edge Function (Production)
模式2——Edge Function(生产)
Trigger: after to a real Supabase project.
supabase functions deploy <name>Validation steps:
- POST to with
$SUPABASE_URL/functions/v1/<name>(do not print the key value)Authorization: Bearer $SUPABASE_ANON_KEY - Assert HTTP 200
- Assert response body is valid JSON with
ok: true - Assert is present in the response body
request_id
Fail signal: HTTP 500 or unstructured body.
Diagnostic: dashboard function logs:
https://supabase.com/dashboard/project/<PROJECT_REF>/functions/<name>/logsDocs:
Do not report done until: HTTP 200 + + confirmed
against the production URL.
ok: truerequest_id触发条件: 向真实Supabase项目执行后。
supabase functions deploy <name>验证步骤:
- 向发送POST请求,携带
$SUPABASE_URL/functions/v1/<name>(不要打印密钥值)Authorization: Bearer $SUPABASE_ANON_KEY - 断言HTTP状态码为200
- 断言响应体为有效的JSON且包含
ok: true - 断言响应体中存在
request_id
失败信号: HTTP 500或非结构化响应体。
诊断方式: 控制台函数日志:
https://supabase.com/dashboard/project/<PROJECT_REF>/functions/<name>/logs文档:
在满足以下条件前,不得报告完成: 针对生产URL确认HTTP 200 + + 存在。
ok: truerequest_idPattern 3 — RPC
模式3——RPC
Trigger: after any or migration that modifies
an RPC.
CREATE OR REPLACE FUNCTIONValidation steps:
- Call via supabase-js:
supabase.rpc('<function_name>', { ...args }) - Assert is null
error - Assert response data contains the expected fields
Fail signal: is present — read , ,
as a unit.
error.codeerror.codeerror.messageerror.hintDiagnostic: if error code is (undefined column), run:
42703sql
SELECT pg_get_functiondef('<schema>.<function_name>(<arg_types>)'::regprocedure);Docs:
Do not report done until: RPC returns no error + response shape is correct.
触发条件: 执行任何或修改RPC的迁移后。
CREATE OR REPLACE FUNCTION验证步骤:
- 通过supabase-js调用:
supabase.rpc('<function_name>', { ...args }) - 断言为null
error - 断言响应数据包含预期字段
失败信号: 存在——需完整读取、、。
error.codeerror.codeerror.messageerror.hint诊断方式: 若错误码为(未定义列),执行:
42703sql
SELECT pg_get_functiondef('<schema>.<function_name>(<arg_types>)'::regprocedure);文档:
在满足以下条件前,不得报告完成: RPC调用无错误 + 响应结构正确。
Pattern 4 — CRUD / Insert
模式4——CRUD / 插入
Trigger: after any insert, update, or delete via supabase-js.
Validation steps:
- Always chain onto the operation
.select().throwOnError() - Assert returned is a non-null array
data - Assert the array contains a row with
id
Why this matters: without sends
— PostgREST returns 204 with empty body. supabase-js
translates this to . This is not confirmation the
row was saved.
.insert().select()Prefer: return=minimal{ data: null, error: null }Correct pattern:
ts
const { data } = await supabase
.from("table")
.insert({ ...values })
.select()
.throwOnError();
// data is a non-null array if the insert succeededDocs:
Do not report done until: insert returns a non-null array containing a row
with .
id触发条件: 通过supabase-js执行任何插入、更新或删除操作后。
验证步骤:
- 必须在操作后链式调用
.select().throwOnError() - 断言返回的为非空数组
data - 断言数组中包含带的行
id
为何这很重要: 不带的会发送——PostgREST会返回204状态码和空响应体。supabase-js会将其转换为,这无法确认行已保存。
.select().insert()Prefer: return=minimal{ data: null, error: null }正确模式:
ts
const { data } = await supabase
.from("table")
.insert({ ...values })
.select()
.throwOnError();
// 插入成功时,data为非空数组文档:
在满足以下条件前,不得报告完成: 插入操作返回包含带行的非空数组。
idPattern 5 — RLS
模式5——RLS
Trigger: after any , ,
, or migration touching RLS.
CREATE POLICYDROP POLICYALTER TABLE ... ENABLE ROW LEVEL SECURITYValidation steps — all three must pass:
- Unauthenticated anon — attempt the restricted operation with a plain anon
key and no auth session → assert blocked (error )
42501 - Authenticated user — same operation with anon key + valid JWT → assert allowed
- service_role — same operation with service_role key → assert allowed (service_role always bypasses RLS)
Diagnostic commands:
sql
-- See all policies on a table
SELECT policyname, cmd, qual, with_check
FROM pg_policies WHERE tablename = '<table>';
-- Confirm RLS is enabled
SELECT relname, relrowsecurity FROM pg_class WHERE relname = '<table>';Key insight: if something works from the Supabase dashboard but fails in the
app, check whether an INSERT policy exists for the role your app uses.
has — it skips policy evaluation entirely.
service_roleBYPASSRLSDocs:
Do not report done until: all three scenarios produce the expected result.
触发条件: 执行任何、、或涉及RLS的迁移后。
CREATE POLICYDROP POLICYALTER TABLE ... ENABLE ROW LEVEL SECURITY验证步骤——必须全部通过:
- 未认证匿名用户——使用普通anon密钥且无认证会话尝试受限操作 → 断言被拦截(错误码)
42501 - 已认证用户——使用anon密钥+有效JWT执行相同操作 → 断言被允许
- service_role——使用service_role密钥执行相同操作 → 断言被允许(service_role始终绕过RLS)
诊断命令:
sql
-- 查看表的所有策略
SELECT policyname, cmd, qual, with_check
FROM pg_policies WHERE tablename = '<table>';
-- 确认RLS已启用
SELECT relname, relrowsecurity FROM pg_class WHERE relname = '<table>';关键提示: 如果在Supabase控制台中操作正常,但应用中失败,请检查应用使用的角色是否有对应的INSERT策略。拥有权限——会完全跳过策略评估。
service_roleBYPASSRLS文档:
在满足以下条件前,不得报告完成: 三种场景均产生预期结果。
Pattern 6 — Schema Migration / Type Drift
模式6——Schema迁移 / 类型漂移
Trigger: after any migration that adds, removes, or renames a column.
Validation steps:
- Run:
supabase gen types typescript --local > supabase/types.gen.ts - Run:
git diff supabase/types.gen.ts - If diff is non-empty → expected (new column). Commit the updated file.
- If diff is empty and you added a column → migration may not have run — check.
Drift detection:
sql
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = '<table>'
ORDER BY ordinal_position;Why this matters: TypeScript compiles cleanly against stale types. A column
that exists in the DB but not in is simply invisible to your code
— no compile error, silent bugs.
types.gen.tsCI: run after any migration deploy. Fail the
build if the diff is non-empty.
supabase gen types typescript --local > supabase/types.gen.ts && git diff --exit-code supabase/types.gen.tsDocs:
Do not report done until: reflects the current live schema
and the file is committed.
types.gen.ts触发条件: 执行任何添加、删除或重命名列的迁移后。
验证步骤:
- 执行:
supabase gen types typescript --local > supabase/types.gen.ts - 执行:
git diff supabase/types.gen.ts - 若差异非空 → 符合预期(新增列)。提交更新后的文件。
- 若差异为空但已添加列 → 迁移可能未运行——需检查。
漂移检测:
sql
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = '<table>'
ORDER BY ordinal_position;为何这很重要: TypeScript会基于过期类型正常编译。数据库中存在但中不存在的列,会在无编译错误的情况下导致代码出现静默错误。
types.gen.tsCI流程: 迁移部署后执行。若差异非空,构建失败。
supabase gen types typescript --local > supabase/types.gen.ts && git diff --exit-code supabase/types.gen.ts文档:
在满足以下条件前,不得报告完成: 反映当前实时Schema且文件已提交。
types.gen.ts