Last Updated: 2026-04-24
Status: Stable
Target Audience: Developers and AI agents (GitHub Copilot, Cursor, general AI assistants) Comprehensive guide for database development in the Encore OS Platform, covering RLS patterns, migrations, custom fields, multi-tenant queries, and debugging.
AI Agent Context
Key Patterns for AI:- Always include
organization_idandcustom_fields JSONB DEFAULT '{}' NOT NULLin business entity tables - Always use
SECURITY DEFINERfunctions for RLS policies to avoid recursion - Always follow naming conventions:
snake_casefor tables/columns,{core}_{action}_{entity}()for functions - Always ship schema changes as versioned
supabase/migrations/*.sql— never manual SQL in production Dashboard - Prefer declarative DDL under
supabase/schemas/plussupabase db diff(see DECLARATIVE_SCHEMA_GUIDE.md); use hand-written migrations for documented diff caveats (DML, some policy/view/grant cases) - Always test RLS policies with multi-tenant isolation scenarios
- Creating recursive RLS policies (querying RLS-protected tables in RLS policies)
- Forgetting
organization_idin business tables (breaks multi-tenancy) - Using CHECK constraints for time-based validations (use triggers or application logic)
- Creating schema changes outside migrations / declarative workflow (no orphan Dashboard DDL)
- Not including
custom_fieldsJSONB column in business entities
Quick Reference
Database Development Workflow
1. Plan the Schema
Before writing SQL, document:- Table purpose and ownership (which core/module)
- Required columns (standard + module-specific)
- Relationships (foreign keys)
- RLS requirements
- Custom fields use cases
2. Author DDL (declarative) and generate a migration
Preferred (DDL):- Edit or add
.sqlundersupabase/schemas/so it reflects the desired final state (see DECLARATIVE_SCHEMA_GUIDE.md). - Optional:
npm run db:schemas:lint(@supabase/pg-topoordering / parse diagnostics; skips if no.sqlfiles yet). - Stop local Supabase, then generate a migration from drift:
Shortcut:
npm run db:schemas:diff -- -f {core}_{entity}_{change} - Review the new file under
supabase/migrations/(watch for unintended drops).
schema vs system-defaults lanes.
3. Write or verify SQL
Follow naming conventions and include:- Table creation with standard columns
- RLS enablement
- RLS policies using
SECURITY DEFINERfunctions - Indexes for performance
- Comments for documentation
4. Test Locally
5. Generate TypeScript Types
6. Update Code
- Create TypeScript types/interfaces
- Implement queries with proper RLS
- Add tests for RLS policies
- Document in implementation log
Creating Tables with RLS
Standard Table Structure
All business entity tables MUST include:When to Include custom_fields
✅ Include for:- Business entities (employees, residents, forms, transactions)
- User-facing data that may need org-specific metadata
- Junction/mapping tables
- Audit/log tables
- System/configuration tables
Example: Employee Table
RLS Policy Patterns
Critical Rule: Use SECURITY DEFINER Functions
❌ NEVER query RLS-protected tables directly in policies (causes infinite recursion):Standard RLS Policies
SELECT Policy (View)
INSERT Policy (Create)
UPDATE Policy (CRITICAL: Must Include WITH CHECK)
WITH CHECK, a user can change organization_id to move data between tenants, breaking multi-tenant isolation.
DELETE Policy
Creator-Only Policies
For entities where only the creator can modify:Role-Based Policies
For policies that require specific roles:Migration Creation and Testing
Migration Naming
Format (new migrations):{timestamp}_{snake_case_description}.sql — e.g. 20260428000143_pf15_picklist_defaults_data_driven.sql. The Lovable-generated {timestamp}_{uuid}.sql pattern is grandfathered for existing files but blocked for new ones by scripts/database/lint-migration-filename.ts in pre-commit.
Migration Requirements
- Monotonic and Ordered: Migrations run in timestamp order
- Re-runnable: Can run multiple times safely (use
IF NOT EXISTS) - Backward-Compatible: When possible, avoid breaking changes
Migration Template
Testing Migrations
Migration Best Practices
- One logical change per migration - Easier to review and rollback
- Use IF NOT EXISTS - Makes migrations re-runnable
- Add comments - Document purpose and changes
- Test locally first - Always test before committing
- Review RLS policies - Ensure tenant isolation
Custom Fields Implementation
When to Use Custom Fields
Usecustom_fields JSONB for:
- Organization-specific metadata on records
- Fields that vary by organization
- Extensibility without schema changes
- Employee badge numbers
- External system IDs
- Custom flags or preferences
- Organization-specific classifications
Custom Fields Pattern
Querying Custom Fields
Custom Fields Indexing
For frequently queried custom fields, add GIN index:- Custom field is queried frequently
- Organization has many records
- Query performance is slow
Multi-Tenant Query Patterns
Always Filter by organization_id
✅ CORRECT: Always include organization_id filter
❌ WRONG: Missing organization_id filter
Getting Current Organization
Multi-Organization Queries
For users with access to multiple organizations:Database Debugging
Check RLS Policies
Test RLS Policies
Debug Query Performance
Common RLS Issues
Issue: “Infinite recursion” error
Cause: Querying RLS-protected table in policy Solution: UseSECURITY DEFINER function
Issue: “No rows returned” when rows exist
Cause: RLS policy too restrictive or missing Solution: Check policy conditions, verify user has org accessIssue: “Cross-organization data leakage”
Cause: MissingWITH CHECK in UPDATE policy
Solution: Add WITH CHECK clause to UPDATE policies
Constitution playbook: PF DDL templates
Canonical SQL and JSON examples for module settings, picklists (PF-15), and custom field definitions (PF-16). The constitution §5.2 states the MUST rules; this section is the extended implementation reference. UI patterns for settings pages: settings-pattern-guide.md.Module settings table ({core}_module_settings)
Picklists: pf_picklists and pf_picklist_items
pf_has_org_access / pf_is_org_admin from your schema):
Custom field definitions: pf_custom_field_definitions
Example validation_rules JSON
Related Documentation
Core Standards
- Constitution §5 - Database rules and RLS requirements
- AGENTS.md - Database naming conventions and patterns
Development Guides
- Supabase best practices - Encore-mapped synthesis of current Supabase guidance (RLS init-plan, security_invoker, SECURITY DEFINER, advisors, declarative schema, edge/Deno, types/parity) with the gates that enforce each
- Settings pattern guide - Module settings pages (UI; pairs with Constitution playbook DDL)
- Troubleshooting Guide - Database connection and query issues
- Testing Setup and Run Guide - RLS testing patterns and execution
Architecture
- Custom Fields Guide - Detailed custom fields patterns
- Constitution - Database development guardrails and migration patterns
Maintained By: Platform Foundation Team