Agent Skillsmarmelab/atomic-crm › backend-dev

backend-dev

GitHub

提供 Atomic CRM 后端开发规范,指导在 Supabase 环境下进行数据库迁移、视图、触发器、RLS 策略及 Edge Functions 的开发与决策。

.claude/skills/backend-dev/SKILL.md marmelab/atomic-crm

Trigger Scenarios

决定是否需要后端逻辑 创建或修改数据库视图、触发器、RLS 策略 编写或更改 Supabase edge function

Install

npx skills add marmelab/atomic-crm --skill backend-dev -g -y
More Options

Non-standard path

npx skills add https://github.com/marmelab/atomic-crm/tree/main/.claude/skills/backend-dev -g -y

Use without installing

npx skills use marmelab/atomic-crm@backend-dev

指定 Agent (Claude Code)

npx skills add marmelab/atomic-crm --skill backend-dev -a claude-code -g -y

安装 repo 全部 skill

npx skills add marmelab/atomic-crm --all -g -y

预览 repo 内 skill

npx skills add marmelab/atomic-crm --list

SKILL.md

Frontmatter
{
    "name": "backend-dev",
    "description": "Coding practices for backend development in Atomic CRM. Use when deciding whether backend logic is needed, or when creating\/modifying database migrations, views, triggers, RLS policies, edge functions, or custom dataProvider methods that call Supabase APIs."
}

Backend Development

Reference for backend conventions. There is no custom backend server all server-side logic uses Supabase: PostgreSQL (tables, views, triggers, RLS), Auth API, Storage, and Edge Functions. Read the relevant section before writing backend code, then check the Red Flags and Verification list.

When to Use

  • Deciding whether a change needs backend logic at all (default: prefer the frontend).
  • Creating or modifying database views, triggers, or RLS policies.
  • Writing or changing a Supabase edge function, or a custom dataProvider method that calls Supabase.

For the SQL migration mechanics themselves, see Skill({skill: "writing-migrations"}). For the frontend side of a dataProvider method, see Skill({skill: "frontend-dev"}).

Deciding where logic belongs

Prefer frontend-only solutions via custom dataProvider methods calling the PostgREST API.

When backend logic is needed:

  • Aggregation/read optimization: Create a database view (CREATE OR REPLACE VIEW in a new migration). PostgREST exposes views like tables. When underlying table columns change, update the contacts_summary and companies_summary views too.
  • Complex mutations (multi-table writes): Create a Supabase edge function in Deno. Stored procedures via RPC are less preferred (code lives in migrations, harder to maintain). On the frontend, expose the edge function as a custom dataProvider method (using httpClient(${supabaseUrl}/functions/v1/)) and call it via react-query. (e.g. salesCreate()/functions/v1/users, mergeContacts()/functions/v1/merge_contacts)

Edge function conventions

  • Shared utils in supabase/functions/_shared/ — reuse authentication.ts, supabaseAdmin.ts, cors.ts, utils.ts
  • Follow the middleware chain pattern: CORS preflight → authenticate() → handler
  • verify_jwt = false in config.toml, so JWT validation is manual via authenticate()

Other conventions

  • New tables need RLS policies and the auto-set sales_id trigger (see migration 20260108160722)

RLS enforcement pitfalls (security-critical)

  • A trigger/function that counts rows to enforce a limit (capacity, quota, balance, subscription depletion) must be SECURITY DEFINER with a fixed search_path, or delegate to one. A SECURITY INVOKER count runs under the caller's RLS, sees only the rows that caller may read, under-counts, and the limit silently never fires (e.g. session_booked_count is DEFINER for exactly this reason).
  • A WITH CHECK clause must constrain every column a non-admin may set, not just ownership. with check (contact_id = current_user_contact()) alone lets the caller forge any other column (status, type, amounts, flags) via PostgREST — privilege escalation / payment bypass. Whitelist allowed values in the policy, a trigger, or a CHECK constraint; never trust a client-chosen status/type.

Red Flags

  • Reaching for an edge function or RPC where a custom dataProvider method against PostgREST would do.
  • Adding a column to a table that feeds contacts_summary/companies_summary without updating the view.
  • A new table without RLS policies or the sales_id trigger.
  • A row-counting limit enforced by a SECURITY INVOKER function it under-counts and never fires.
  • A WITH CHECK that constrains only ownership while leaving status/type/amounts client-settable.
  • An edge function handler that runs before authenticate() in the middleware chain.

Verification

  • The logic genuinely needs the backend no frontend-only path was available.
  • New/changed views keep contacts_summary and companies_summary in sync.
  • New tables have RLS policies and the sales_id trigger.
  • Any limit-enforcing count runs SECURITY DEFINER with a fixed search_path.
  • Every column a non-admin can set is constrained by policy, trigger, or CHECK.
  • Edge functions follow CORS → authenticate() → handler.

Version History

  • 167a4cd Current 2026-08-20 13:19

Same Skill Collection

.claude/skills/adr-writing/SKILL.md
.claude/skills/delete-initial-resource/SKILL.md
.claude/skills/e2e-conventions/SKILL.md
.claude/skills/frontend-dev/SKILL.md
.claude/skills/grill-me/SKILL.md
.claude/skills/playwright-testing/SKILL.md
.claude/skills/ponytail-audit/SKILL.md
.claude/skills/ponytail-debt/SKILL.md
.claude/skills/ponytail-help/SKILL.md
.claude/skills/ponytail-review/SKILL.md
.claude/skills/resolving-rollback-conflicts/SKILL.md
.claude/skills/setup-interview/SKILL.md
.claude/skills/shadcn-customization/SKILL.md
.claude/skills/update-branding/SKILL.md
.claude/skills/worktree-detection/SKILL.md
.claude/skills/ponytail/SKILL.md

Metadata

Files
0
Version
167a4cd
Hash
c103470e
Indexed
2026-08-20 13:19

- 위키
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-25 11:06
浙ICP备14020137号-1 $방문자$