Database Schema & Relationships
A complete reference for CareNova's database — all tables organized by domain, column definitions, and the relationships between them.
Written By Dev010
Last updated 19 days ago
CareNova's entire database is defined in a single file — lib/db/schema.ts. This is the source of truth for every table, column, type, and relationship in the system. The master SQL installation file INSTALL.sql is generated from this schema and can be run directly in Supabase to create everything from scratch.
Entity Relationship Overview
The core of the data model revolves around four central entities. Everything else connects to one or more of them.
clinics
└── landing_page_settings
departments
├── users (department_id)
├── patients (department_id)
├── services (department_id)
├── staff (department_id)
├── expenses (department_id)
└── test_categories (department_id)
users (staff accounts / login identities)
├── patients (primary_doctor_id)
├── appointments (doctor_id)
├── medical_records (doctor_id)
├── clinical_notes (author_id)
├── diagnoses (doctor_id)
├── medical_record_vitals (recorded_by_id)
├── prescriptions (doctor_id)
├── test_reports (doctor_id)
├── invoices (doctor_id)
├── expenses (submitted_by)
├── odontograms (doctor_id)
├── blog_posts (author_id)
├── notifications (user_id)
└── payroll (staff_id)
patients
├── appointments (patient_id)
├── medical_records (patient_id)
├── medical_record_vitals (patient_id)
├── clinical_notes (patient_id)
├── diagnoses (patient_id)
├── medical_attachments (patient_id)
├── prescriptions (patient_id)
├── test_reports (patient_id)
├── invoices (patient_id)
├── payments (patient_id)
└── odontograms (patient_id)Enum Types
CareNova defines five PostgreSQL enums:
Core Tables
clinics
Stores the clinic's identity and branding configuration. One row per installation — created automatically by INSTALL.sql.
users
Represents all staff accounts. The id column matches the auth.users.id from Supabase Auth — this is how the authenticated session maps to an application user record.
approved_at being null means the user has confirmed their email but has not yet been approved by an admin. The middleware blocks dashboard access until this is set.
departments
Organizational units within the clinic. Used to group staff, patients, services, and expenses.
patients
Central patient record. The most referenced table in the system — almost every clinical and financial table has a patient_id foreign key.
Scheduling
appointments
Links a patient to a doctor and service for a specific time slot.
services
Defines the treatments and services the clinic offers.
Clinical Records
medical_records
A visit record that groups clinical data for one patient encounter. Acts as a parent for vitals, notes, diagnoses, and attachments.
medical_record_vitals
clinical_notes
diagnoses
medical_attachments
prescriptions
odontograms
Dental charting — dental clinic type only.
Laboratory
lab_vendors
External laboratory suppliers.
test_categories → test_methodologies → laboratory_tests
Lab tests follow a hierarchy:
test_categories
├── test_methodologies (category_id)
├── turnaround_times (category_id)
├── sample_types (category_id)
└── laboratory_tests (category_id)
├── sample_type_id → sample_types
├── methodology_id → test_methodologies
└── turnaround_time_id → turnaround_timestest_reports
Patient-specific lab results.
Financial
invoices
The UNIQUE constraint on appointment_id enforces one invoice per appointment.
invoice_items
Line items belonging to an invoice.
payments
expenses
Operations
inventory
staff
HR records — separate from users.
payroll
Auth & Security Tables
role_permissions
Stores the admin-configurable permission matrix.
Unique constraint on (role, permission_key) prevents duplicate entries.
auth_audit_log
Records every auth event for security auditing.
login_attempts
Used for rate limiting — tracks failed login attempts by email and IP address.
user_sessions
Tracks active sessions for visibility and revocation.
Blog
blog_categories → blog_posts → blog_comments
blog_categories
└── blog_posts (category_id, SET NULL)
└── blog_comments (post_id, CASCADE)Content & Notifications
landing_page_settings
One row per clinic — all jsonb columns store structured settings objects per tab (branding, colors, content, seo, contact, social).
notifications
Per-user notification records used by the notification drawer.
Cascade Behavior Reference
Understanding what happens to related records when a parent is deleted is critical before removing any record in production.
Before deleting any core record in production, query related tables first to understand the full impact. Cascade deletes are immediate and cannot be undone.