Skip to main content
Version: 1.0.0
Last Updated: 2025-12-31
Status: ✅ Complete
This guide explains how to use custom_fields JSONB columns to extend Encore OS business entities with organization-specific metadata.

1. Purpose & Design Philosophy

Why Custom Fields?

Organizations have diverse workflows that can’t be predicted upfront:
  • Recovery housing orgs may track custom resident phases, external case IDs, or funding sources
  • Healthcare providers may need custom credential categories, shift codes, or billing codes
  • Multi-site operators may have site-specific identifiers, reporting codes, or compliance flags
Design Principle: Rather than adding dozens of nullable columns or creating new tables for every edge case, custom_fields provides a type-safe, tenant-isolated extension point.

What Custom Fields Are NOT

  • NOT a replacement for core schema design: Frequently-queried fields should be proper columns
  • NOT a dumping ground: Use for organization-specific business data, not technical config
  • NOT a security bypass: custom_fields inherits table’s RLS policies

2. When to Use Custom Fields

Decision Tree

Examples by Category


3. Implementation Patterns

3.1 Adding Custom Fields to New Tables

Migration Template:

3.2 Adding Custom Fields to Existing Tables

Migration Template:

3.3 Backend: Querying Custom Fields (TypeScript + Supabase)

Basic Queries:
Update Custom Fields:

3.4 Frontend: Rendering Custom Fields (React)

Type-Safe Custom Fields:
Generic Custom Fields Editor:

4. Performance Considerations

When to Add GIN Indexes

Default: No index required for occasional queries. Add GIN index if:
  • Frequently filtering by custom_fields (e.g., dashboard queries)
  • Complex JSONB queries (containment, existence checks)
  • Query performance p95 > 500ms with EXPLAIN showing sequential scans
Migration:
Performance Testing:

5. Security & RLS

Key Principle

custom_fields inherits table’s RLS policies. No separate RLS required. Example:

What NOT to Store

Prohibited in custom_fields:
  • ❌ Passwords, API keys, secrets
  • ❌ Social Security Numbers, full credit card numbers
  • ❌ Unencrypted PHI (medical diagnoses, treatment notes)
  • ❌ Data requiring separate access control from parent entity
Allowed:
  • ✅ Badge numbers, employee IDs (non-sensitive identifiers)
  • ✅ External system IDs (integration references)
  • ✅ Workflow metadata (priority, status, dates)
  • ✅ Business codes (cost centers, project codes, shift codes)

6. Testing Custom Fields

Unit Tests

RLS Tests


7. Migration Checklist

When adding custom_fields to a table:
  • Add column: ADD COLUMN IF NOT EXISTS custom_fields JSONB DEFAULT '{}' NOT NULL
  • Add comment with 3-5 example use cases
  • Verify RLS policies cover custom_fields (inherited automatically)
  • Update TypeScript types if defining known custom fields
  • Add integration tests for custom_fields persistence
  • Add RLS tests for custom_fields tenant isolation
  • Document in specs/IMPLEMENTATION_LOG.md
  • Consider GIN index if frequently querying (defer until performance issue)

8. Common Patterns by Module

Platform Foundation (PF)

pf_profiles:
pf_documents:

Forms & Workflow (FW)

fw_form_submissions:
fw_automation_rules:

Workforce & HR (HR)

hr_employees:
hr_onboarding_instances:
hr_offboarding_instances:

9. Troubleshooting

Issue: Query Performance Slow

Symptom: Queries filtering by custom_fields take >500ms Solution:
  1. Check EXPLAIN ANALYZE for sequential scans
  2. Add GIN index if frequently querying:
  3. Consider promoting frequently-queried field to proper column

Issue: Custom Fields Not Persisting

Symptom: Updates to custom_fields don’t save Debugging:
  1. Check RLS policies allow UPDATE on table
  2. Verify column exists: SELECT custom_fields FROM {table} LIMIT 1
  3. Check for TypeScript type errors in mutation

Issue: RLS Blocking Custom Fields Access

Symptom: Can’t read custom_fields even though user should have access Debugging:
  1. Verify table has RLS policies
  2. Test with service role key (bypasses RLS) to confirm data exists
  3. Check RLS policies include necessary conditions
  4. Remember: custom_fields inherits table RLS, no separate policy needed

10. Future Enhancements

Planned:
  • Custom fields UI builder for admins (per-organization field definitions)
  • Validation rules for custom fields (e.g., badge_number must match regex)
  • Custom fields search across all tables
  • Custom fields analytics/reporting
  • Export custom fields schema per organization
Not Planned:
  • ❌ Promoting all custom fields to columns (defeats extensibility purpose)
  • ❌ Custom RLS per custom field (inherits table RLS)
  • ❌ Cross-organization custom field sharing (violates tenant isolation)

End of Custom Fields Guide