Claude Code Plugins

Community-maintained marketplace

Feedback

crispy-data-provider

@krwhynot/crispy-crm
1
0

Data Provider architecture rules for Crispy CRM. Use when creating data handlers, adding new resources, modifying unifiedDataProvider, implementing CRUD operations, or working with React Admin handlers and Supabase queries. Enforces Strangler Fig migration pattern, View/Table duality, validation boundaries, and service layer encapsulation.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name crispy-data-provider
description Data Provider architecture rules for Crispy CRM. Use when creating data handlers, adding new resources, modifying unifiedDataProvider, implementing CRUD operations, or working with React Admin handlers and Supabase queries. Enforces Strangler Fig migration pattern, View/Table duality, validation boundaries, and service layer encapsulation.
version 1.0.0

Crispy CRM Data Provider Architecture

Apply these rules when modifying the data provider layer (src/atomic-crm/providers/supabase).

Quick Reference

Rule Summary
Migration New resources in handlers/, shrink unifiedDataProvider.ts
Read/Write Read from Views, Write to Tables
Validation Zod at Handler boundary, NOT in forms
Business Logic Services, not raw Supabase calls
Deletes Soft delete (deleted_at) unless explicitly told otherwise

1. Migration Strategy (Strangler Fig)

Goal: Incrementally replace unifiedDataProvider.ts without breaking the app.

Rules

  • Maintain unifiedDataProvider.ts: Only apply bug fixes. No new logic.
  • Isolate New Resources: All new resources use Composed Handler Pattern in handlers/.
  • Shrink the Monolith: unifiedDataProvider.ts should only decrease in size.

Decision Tree

Is this a NEW resource?
  YES → Create handler in src/providers/supabase/handlers/
  NO → Is this a CRITICAL bug fix?
    YES → Patch minimally in unifiedDataProvider.ts
    NO → Is this ASSIGNED migration work?
      YES → Create handler, migrate, delete from monolith
      NO → Do not modify unifiedDataProvider.ts

See migration-strategy.md for details.


2. View vs. Table Duality

Problem: React Admin expects one "resource" but we have Views (read) and Tables (write).

Rules

  • Read from Views: getList, getOne query SQL Views (e.g., contacts_summary)
  • Write to Tables: create, update go to Base Tables (e.g., contacts)
  • Strip Computed Fields: Remove view-only fields before writing

Example

// READ - Use the View
const getList = async () => {
  const { data } = await supabase.from('contacts_summary').select('*');
  return { data, total: data.length };
};

// WRITE - Use the Table
const create = async (params) => {
  const cleaned = stripComputedFields(params.data);
  const { data } = await supabase.from('contacts').insert(cleaned);
  return { data };
};

See view-table-duality.md for implementation.


3. Validation Boundaries

Principle: Validate at the API boundary, NOT in forms.

Rules

  • Handler Validates: Use Zod schemas from src/atomic-crm/validation/
  • Fail Fast: Throw HttpError immediately on validation failure
  • Form Trust: Forms may display errors but don't enforce validation

Error Response Format

throw new HttpError(400, "Validation Error", {
  errors: {
    email: "Invalid email format",
    first_name: "Required"
  }
});

See error-handling.md for mapping patterns.


4. Service Layer Encapsulation

Principle: Data Provider translates; Services orchestrate.

Rules

  • Provider = Translator: Convert React Admin params → Supabase params
  • Service = Orchestrator: Handle multi-step operations, business logic
  • No Raw Calls: Complex logic belongs in Services, not Handlers

When to Use Services

  • Multi-table operations (e.g., "Create Opportunity + Link Products")
  • Business rule enforcement (e.g., "Only 5 opportunities per contact")
  • Cross-cutting concerns (e.g., audit logging)

Example

// Handler - Simple translation
const create = async (params) => {
  return opportunitiesService.createWithProducts(params.data);
};

// Service - Business logic
class OpportunitiesService {
  async createWithProducts(data) {
    const opportunity = await this.createOpportunity(data);
    await this.linkProducts(opportunity.id, data.product_ids);
    await this.logActivity(opportunity.id, 'created');
    return opportunity;
  }
}

5. Data Safety & Simplicity

Soft Deletes (Always)

// CORRECT
await supabase.from('contacts').update({ deleted_at: new Date() }).eq('id', id);

// WRONG - Never hard delete unless explicitly told
await supabase.from('contacts').delete().eq('id', id);

Explicit Composition (No Magic)

// CORRECT - Explicit composition order
export const createTasksHandler = () => {
  return withErrorLogging(           // 3. Outer: Catch errors
    withLifecycleCallbacks(          // 2. Middle: Transform
      withValidation(baseHandler),   // 1. Inner: Validate first
      tasksCallbacks
    )
  );
};

// WRONG - Auto-CRUD factory
export const tasksHandler = createAutoHandler('tasks'); // Hidden magic

See handler-patterns.md for composition examples.


Code Review Checklist

Before committing data provider changes, verify:

  • New logic in handlers/ (not unifiedDataProvider.ts)?
  • Writes target Base Table (not View)?
  • Zod validation at Handler boundary?
  • Business logic in Service layer?
  • Errors mapped to React Admin format?
  • Soft delete used (not hard delete)?
  • Handler composition is explicit?

Anti-Patterns (BLOCK)

Anti-Pattern Correct Approach
Adding to unifiedDataProvider.ts Create new handler
Writing to View Write to Table, strip computed fields
Form-level Zod validation Handler-level validation
Raw supabase.from() in handlers Service layer for complex ops
Hard delete Soft delete with deleted_at
Auto-CRUD factories Explicit handler composition

Reference Files

For detailed implementation guidance:


Skill Status: COMPLETE Line Count: < 200 (following 500-line rule) Progressive Disclosure: Reference files for implementation details