Skip to main content

backend-dev

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.

소스 정보

저장소
marmelab/atomic-crm
최근 소스 활동
2026년 6월 18일 15:50
감지된 SKILL.md 언어
영어
스타
1,293
포크
821

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
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/<name>`)`) 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.
GitHub에서 보기