crispy-data-provider | Skill Performance & Reviews | TopRankSkills

TopRank Skills

Home / Skills / tools / crispy-data-provider

crispy-data-provider

maintained by krwhynot

star 1 account_tree 0 verified_user MIT License
bolt View GitHub

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

chat Comments (0)

chat_bubble_outline

No comments yet. Be the first to share your thoughts!

Skill Details

GitHub Stars 1
GitHub Forks 0
Created Jan 2026
Last Updated il y a 5 mois
tools tools productivity tools

Related Skills

ui-ux-pro-max
chevron_right
dagger-design-proposals
chevron_right
planning-with-files
chevron_right
agent-browser
chevron_right
building-agents
chevron_right

Build your own?

Join 12,000+ developers contributing to the Claude ecosystem.