Skip to main content
RapidDev - Software Development Agency

How to Build a Medical Records App with Lovable

Build a secure medical records app in Lovable with strict Supabase RLS ensuring providers only see their assigned patients and patients only see their own records. You get tabbed patient charts, appointment and prescription management, encrypted document storage, and audit logging — with a HIPAA disclaimer required for any real clinical use. This is an advanced build due to the security requirements.

What you'll build

  • Patient database with demographic and medical history records
  • Strict RLS: providers see only assigned patients, patients see only their own data
  • Tabbed patient chart view with Appointments, Prescriptions, Documents, and Notes sections
  • Appointment scheduling with calendar and status tracking
  • Prescription management with active/inactive status and renewal tracking
  • Encrypted document storage for medical files in a private Supabase Storage bucket
  • Complete audit log of every record access and modification
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced18 min read4-5 hoursLovable Pro or higherApril 2026RapidDev Engineering Team
TL;DR

Build a secure medical records app in Lovable with strict Supabase RLS ensuring providers only see their assigned patients and patients only see their own records. You get tabbed patient charts, appointment and prescription management, encrypted document storage, and audit logging — with a HIPAA disclaimer required for any real clinical use. This is an advanced build due to the security requirements.

What you're building

A medical records application manages the core data of clinical care: patient demographics, appointment history, prescriptions, and uploaded medical documents. The most critical requirement is data isolation — a provider should never accidentally see records belonging to patients they are not assigned to, and patients should only ever see their own chart.

This build implements that isolation at the database level using Supabase Row Level Security, which enforces access rules even if there are bugs in application code. The policies are role-aware: a user with the 'provider' role sees patients they have an explicit provider_patient relationship with. A user with the 'patient' role sees only records where patient_id matches their own profile. Admins (practice managers) have full access.

IMPORTANT DISCLAIMER: This application is for demonstration and learning purposes. It is not HIPAA-compliant out of the box. If you intend to store real patient data in a production environment, you must complete a full HIPAA compliance assessment, sign a Business Associate Agreement (BAA) with your hosting and database providers, implement additional security controls, and consult with a healthcare compliance specialist. Supabase and Lovable do not provide HIPAA BAAs on standard plans.

Final result

A functional medical records application with role-based data isolation, patient chart management, and secure document storage — suitable for demonstration or internal development use with a clear HIPAA compliance path.

Tech stack

LovableFrontend builder and code generation
Supabase PostgreSQLSecure database for patients, appointments, prescriptions
Supabase AuthUser authentication for providers and patients
Supabase StorageEncrypted private bucket for medical documents
shadcn/uiTabs, DataTable, Form, Card, Badge, Dialog components
React Hook Form + ZodMedical form validation with strict field requirements

Prerequisites

  • A Lovable Pro account (required for Edge Functions and multi-table Supabase setups)
  • A Supabase project with URL and anon key ready
  • Understanding that this demo build is NOT HIPAA-compliant for real patient data without additional compliance work
  • A Lovable project connected to Supabase via Cloud tab
  • Basic familiarity with Supabase RLS concepts before starting this advanced build

Build steps

1

Set up the secure database schema with role-based RLS

The schema is the most important part of this build. Create tables for profiles (with roles), patients, provider_patients (the assignment table), appointments, prescriptions, medical_documents, and audit_logs. Enable RLS on every table immediately. The provider_patients junction table is what makes the access control work.

supabase-schema.sql
1-- Run in Supabase SQL Editor
2-- IMPORTANT: This is for demonstration only. Not for use with real patient data without HIPAA compliance.
3
4CREATE TYPE user_role AS ENUM ('patient', 'provider', 'admin');
5CREATE TYPE appointment_status AS ENUM ('scheduled', 'confirmed', 'completed', 'cancelled', 'no_show');
6CREATE TYPE prescription_status AS ENUM ('active', 'completed', 'discontinued', 'expired');
7
8CREATE TABLE profiles (
9 id UUID REFERENCES auth.users PRIMARY KEY,
10 full_name TEXT NOT NULL,
11 email TEXT NOT NULL,
12 role user_role NOT NULL DEFAULT 'patient',
13 specialty TEXT,
14 created_at TIMESTAMPTZ DEFAULT now()
15);
16
17CREATE TABLE patients (
18 id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
19 user_id UUID REFERENCES auth.users UNIQUE,
20 first_name TEXT NOT NULL,
21 last_name TEXT NOT NULL,
22 date_of_birth DATE NOT NULL,
23 gender TEXT,
24 phone TEXT,
25 email TEXT,
26 address TEXT,
27 emergency_contact_name TEXT,
28 emergency_contact_phone TEXT,
29 blood_type TEXT,
30 allergies TEXT[],
31 medical_history TEXT,
32 insurance_provider TEXT,
33 insurance_id TEXT,
34 created_at TIMESTAMPTZ DEFAULT now(),
35 updated_at TIMESTAMPTZ DEFAULT now()
36);
37
38CREATE TABLE provider_patients (
39 provider_id UUID REFERENCES auth.users NOT NULL,
40 patient_id UUID REFERENCES patients(id) NOT NULL,
41 assigned_at TIMESTAMPTZ DEFAULT now(),
42 PRIMARY KEY (provider_id, patient_id)
43);
44
45CREATE TABLE appointments (
46 id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
47 patient_id UUID REFERENCES patients(id) ON DELETE CASCADE NOT NULL,
48 provider_id UUID REFERENCES auth.users NOT NULL,
49 scheduled_at TIMESTAMPTZ NOT NULL,
50 duration_minutes INTEGER NOT NULL DEFAULT 30,
51 status appointment_status DEFAULT 'scheduled',
52 appointment_type TEXT NOT NULL,
53 chief_complaint TEXT,
54 notes TEXT,
55 created_at TIMESTAMPTZ DEFAULT now()
56);
57
58CREATE TABLE prescriptions (
59 id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
60 patient_id UUID REFERENCES patients(id) ON DELETE CASCADE NOT NULL,
61 provider_id UUID REFERENCES auth.users NOT NULL,
62 medication_name TEXT NOT NULL,
63 dosage TEXT NOT NULL,
64 frequency TEXT NOT NULL,
65 start_date DATE NOT NULL,
66 end_date DATE,
67 status prescription_status DEFAULT 'active',
68 instructions TEXT,
69 refills_remaining INTEGER DEFAULT 0,
70 created_at TIMESTAMPTZ DEFAULT now()
71);
72
73CREATE TABLE medical_documents (
74 id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
75 patient_id UUID REFERENCES patients(id) ON DELETE CASCADE NOT NULL,
76 uploaded_by UUID REFERENCES auth.users NOT NULL,
77 document_name TEXT NOT NULL,
78 document_type TEXT NOT NULL,
79 storage_path TEXT NOT NULL,
80 file_size INTEGER,
81 created_at TIMESTAMPTZ DEFAULT now()
82);
83
84CREATE TABLE audit_logs (
85 id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
86 user_id UUID REFERENCES auth.users NOT NULL,
87 action TEXT NOT NULL,
88 table_name TEXT NOT NULL,
89 record_id UUID,
90 patient_id UUID REFERENCES patients(id),
91 ip_address INET,
92 created_at TIMESTAMPTZ DEFAULT now()
93);
94
95-- Enable RLS on ALL tables
96ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
97ALTER TABLE patients ENABLE ROW LEVEL SECURITY;
98ALTER TABLE provider_patients ENABLE ROW LEVEL SECURITY;
99ALTER TABLE appointments ENABLE ROW LEVEL SECURITY;
100ALTER TABLE prescriptions ENABLE ROW LEVEL SECURITY;
101ALTER TABLE medical_documents ENABLE ROW LEVEL SECURITY;
102ALTER TABLE audit_logs ENABLE ROW LEVEL SECURITY;
103
104-- Helper: check if current user is a provider assigned to a patient
105CREATE OR REPLACE FUNCTION is_my_patient(p_patient_id UUID)
106RETURNS BOOLEAN LANGUAGE sql STABLE SECURITY DEFINER AS $$
107 SELECT EXISTS (
108 SELECT 1 FROM provider_patients
109 WHERE provider_id = auth.uid() AND patient_id = p_patient_id
110 );
111$$;
112
113-- Helper: get patient_id for current user if they are a patient
114CREATE OR REPLACE FUNCTION my_patient_id()
115RETURNS UUID LANGUAGE sql STABLE SECURITY DEFINER AS $$
116 SELECT id FROM patients WHERE user_id = auth.uid() LIMIT 1;
117$$;
118
119-- Patients: providers see assigned patients, patients see own record, admins see all
120CREATE POLICY "Providers see assigned patients" ON patients FOR SELECT USING (
121 is_my_patient(id) OR
122 user_id = auth.uid() OR
123 EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin')
124);
125CREATE POLICY "Providers update assigned patients" ON patients FOR UPDATE USING (is_my_patient(id));
126CREATE POLICY "Admins manage patients" ON patients FOR ALL USING (
127 EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin')
128);
129
130-- Appointments: same access as patients
131CREATE POLICY "Providers see assigned patient appointments" ON appointments FOR SELECT USING (
132 is_my_patient(patient_id) OR patient_id = my_patient_id() OR
133 EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin')
134);
135CREATE POLICY "Providers manage appointments" ON appointments FOR ALL USING (provider_id = auth.uid());
136
137-- Prescriptions: same access pattern
138CREATE POLICY "Authorized access to prescriptions" ON prescriptions FOR SELECT USING (
139 is_my_patient(patient_id) OR patient_id = my_patient_id() OR
140 EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin')
141);
142CREATE POLICY "Providers create prescriptions" ON prescriptions FOR INSERT WITH CHECK (provider_id = auth.uid());
143CREATE POLICY "Providers update own prescriptions" ON prescriptions FOR UPDATE USING (provider_id = auth.uid());
144
145-- Documents
146CREATE POLICY "Authorized access to documents" ON medical_documents FOR SELECT USING (
147 is_my_patient(patient_id) OR patient_id = my_patient_id() OR
148 EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin')
149);
150CREATE POLICY "Providers upload documents" ON medical_documents FOR INSERT WITH CHECK (
151 is_my_patient(patient_id)
152);
153
154-- Audit logs: users see own logs, admins see all
155CREATE POLICY "Users see own audit logs" ON audit_logs FOR SELECT USING (user_id = auth.uid());
156CREATE POLICY "System inserts audit logs" ON audit_logs FOR INSERT WITH CHECK (user_id = auth.uid());
157CREATE POLICY "Admins see all audit logs" ON audit_logs FOR SELECT USING (
158 EXISTS (SELECT 1 FROM profiles WHERE id = auth.uid() AND role = 'admin')
159);

Pro tip: The is_my_patient() and my_patient_id() SECURITY DEFINER functions are critical — they allow RLS policies to check relationships without causing infinite recursion in the policy evaluation. Always test these functions by running SELECT is_my_patient('some-uuid') in the SQL Editor with different authenticated users.

Expected result: All tables created with RLS enabled. The two SQL helper functions appear in Supabase Dashboard under Database → Functions. Attempting to SELECT from patients without a matching provider_patients row returns 0 rows, not an error.

2

Build authentication with role selection and patient registration

The app needs distinct login flows for providers and patients. After sign-up, users select their role and complete their profile. Providers get a provider view; patients get a patient view. Admin accounts are created manually in Supabase.

prompt.txt
1Build the authentication and onboarding flow:
2
31. Login/Register page:
4 - Two tabs: "Sign In" and "Create Account"
5 - Email + password fields for both
6 - On register: after supabase.auth.signUp(), show a role selection step
7
82. Role selection step (after sign-up):
9 - Two large clickable cards: "I am a Healthcare Provider" and "I am a Patient"
10 - Provider card: shows stethoscope icon, fields for full_name and specialty
11 - Patient card: shows user icon, fields for full_name
12 - On select: insert into profiles table with chosen role
13
143. Patient registration form (shown to new users with role='patient'):
15 - Multi-step form collecting: personal details (name, DOB, gender, phone, email)
16 - Medical info (blood_type, allergies as comma-separated input, insurance details)
17 - Emergency contact
18 - Insert into patients table with user_id = current user
19
204. Route guards:
21 - /provider/* routes: check profiles.role = 'provider', redirect patients to /patient
22 - /patient/* routes: check profiles.role = 'patient', redirect providers to /provider
23 - /admin/* routes: check role = 'admin'
24
255. After completing onboarding, redirect to role-appropriate dashboard

Expected result: New provider accounts land on the provider dashboard after registration. New patient accounts complete the patient registration form and land on their patient portal. Trying to access /provider as a patient redirects to /patient.

3

Build the patient chart view with tabbed sections

The patient chart is the central view for providers. It shows a patient summary header with key vitals and demographics, then tabs for Appointments, Prescriptions, Documents, and Notes. Add audit logging on every view of a patient chart.

prompt.txt
1Build the patient chart view at /provider/patients/[patientId]:
2
31. Patient header card (full width, above tabs):
4 - Patient photo placeholder (Avatar with initials)
5 - Name, DOB, age (calculated), gender, blood type
6 - Allergy badges (shadcn Badge, red) show "No Known Allergies" if empty
7 - Insurance info
8 - Emergency contact
9 - Edit button (opens Dialog with full patient edit form)
10
112. shadcn Tabs below the header:
12
13 APPOINTMENTS tab:
14 - DataTable: Date/Time, Type, Status (Badge), Chief Complaint, Notes
15 - "Schedule Appointment" button opening Dialog with: type select, date/time picker, duration select, chief complaint textarea
16 - Status update: click status cell to change (dropdown: scheduled/confirmed/completed/cancelled/no_show)
17
18 PRESCRIPTIONS tab:
19 - DataTable: Medication, Dosage, Frequency, Start Date, End Date, Status Badge, Refills
20 - Status Badge: active=green, completed=gray, discontinued=red, expired=orange
21 - "Add Prescription" button opening Dialog with all prescription fields
22 - "Discontinue" action sets status=discontinued
23
24 DOCUMENTS tab:
25 - Card grid: document name, type icon, date, size, Download button
26 - "Upload Document" button: file dropzone (PDF, JPG, PNG, DICOM), document type select
27 - Upload to Supabase Storage at path: medical/{patientId}/{timestamp}-{filename} (private bucket)
28 - Download: generate signed URL expiring in 15 minutes
29
30 NOTES tab:
31 - Chronological list of clinical notes
32 - Textarea to add new note with timestamp
33 - Notes stored in a patient_notes table
34
353. On page load: insert an audit_log record: action='view_patient_chart', patient_id=patientId

Pro tip: Insert audit log records using the service role key via an Edge Function rather than directly from the frontend. This ensures logs can't be tampered with by frontend code and captures server-side metadata like IP address.

Expected result: The patient chart loads with the summary header. All tabs render with correct data. Uploading a document shows it in the Documents tab. An audit_logs record is created on each page visit.

4

Build the provider dashboard with patient list

Providers need a dashboard showing their assigned patients, upcoming appointments for the day, and any prescriptions expiring soon. The patient list filters to only show their assigned patients via RLS.

prompt.txt
1Build the provider dashboard at /provider/dashboard:
2
31. Stats row (4 shadcn Cards):
4 - My Patients (count of provider_patients for current provider)
5 - Today's Appointments (count where scheduled_at date = today and provider_id = current)
6 - Active Prescriptions (count of active prescriptions for my patients)
7 - Expiring Soon (prescriptions expiring in next 7 days)
8
92. Today's Schedule section:
10 - Timeline list of today's appointments sorted by time
11 - Each item: time, patient name, appointment type, status badge
12 - Quick status update buttons: Confirm, Mark Complete, Cancel
13 - "No appointments today" empty state
14
153. My Patients list:
16 - shadcn DataTable: Patient Name, DOB/Age, Last Visit, Active Prescriptions count, Actions
17 - Actions: "View Chart" button linking to /provider/patients/{id}
18 - Search input filtering by patient name
19 - "Add Patient" button: search by email to find existing patient, then insert into provider_patients
20
214. Expiring Prescriptions alert section (only shown if count > 0):
22 - shadcn Alert with warning icon
23 - List of prescriptions expiring in 7 days with patient names and renewal buttons

Expected result: The provider dashboard shows accurate stats for the logged-in provider only. The patient list shows only assigned patients. Today's schedule shows correctly timed appointments. The Expiring Prescriptions section appears only when relevant.

5

Build the patient self-service portal

Patients get a read-only view of their own records. They can see their appointments, active prescriptions, and download their own documents. They cannot edit clinical records — only providers and admins can do that.

prompt.txt
1Build the patient portal at /patient/my-records:
2
31. Patient header card:
4 - Patient's own name and basic demographics
5 - Edit button for non-clinical fields only: phone, address, emergency contact
6 - Allergies listed as read-only badges
7
82. shadcn Tabs:
9
10 MY APPOINTMENTS tab:
11 - DataTable: Date, Provider Name, Type, Status Badge, Notes (read-only)
12 - "Request Appointment" button: opens a Dialog with preferred dates/times and reason
13 - Creates an appointment with status='scheduled' (provider must confirm)
14
15 MY MEDICATIONS tab:
16 - Card grid for active prescriptions: medication name, dosage, frequency, refills remaining
17 - Status badges
18 - Read-only cannot be edited by patient
19
20 MY DOCUMENTS tab:
21 - List of uploaded documents: name, date, type
22 - Download button generating 15-minute signed URL from Supabase Storage
23 - Message: "Documents are uploaded by your healthcare provider"
24
25 MY PROFILE tab:
26 - Read-only view of all patient demographics
27 - Edit button for contact information only (phone, address, emergency contact)
28 - Medical information (allergies, blood type, insurance) shows with an info note: "Contact your provider to update medical information"
29
303. Page header banner: "This portal is for informational purposes only. In a medical emergency, call 911 or your local emergency number."
31
32All data fetches use patient's own user_id. RLS ensures they can only see their own records.

Pro tip: The HIPAA disclaimer banners are not just legal boilerplate — they're essential for any real medical application. Ask Lovable to make them prominent but non-intrusive, perhaps as a sticky alert at the top of every patient-facing page.

Expected result: Patients see only their own appointments, medications, and documents. The 'Request Appointment' dialog submits successfully. Downloading a document generates a short-lived signed URL. Attempting to edit clinical information shows an informational message instead of edit controls.

Complete code

src/components/patients/AllergyList.tsx
1import { Badge } from '@/components/ui/badge';
2import { AlertTriangle } from 'lucide-react';
3import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
4
5interface AllergyListProps {
6 allergies: string[] | null;
7 showIcon?: boolean;
8 maxDisplay?: number;
9}
10
11export function AllergyList({ allergies, showIcon = true, maxDisplay = 5 }: AllergyListProps) {
12 if (!allergies || allergies.length === 0) {
13 return (
14 <span className="text-sm text-muted-foreground italic">No Known Allergies</span>
15 );
16 }
17
18 const displayed = allergies.slice(0, maxDisplay);
19 const remaining = allergies.length - maxDisplay;
20
21 return (
22 <div className="flex flex-wrap items-center gap-1">
23 {showIcon && (
24 <AlertTriangle className="h-4 w-4 text-red-500 shrink-0" aria-hidden="true" />
25 )}
26 {displayed.map((allergy) => (
27 <Badge
28 key={allergy}
29 variant="destructive"
30 className="text-xs font-normal"
31 >
32 {allergy}
33 </Badge>
34 ))}
35 {remaining > 0 && (
36 <TooltipProvider>
37 <Tooltip>
38 <TooltipTrigger asChild>
39 <Badge variant="outline" className="text-xs cursor-help">
40 +{remaining} more
41 </Badge>
42 </TooltipTrigger>
43 <TooltipContent>
44 <p className="text-xs">{allergies.slice(maxDisplay).join(', ')}</p>
45 </TooltipContent>
46 </Tooltip>
47 </TooltipProvider>
48 )}
49 </div>
50 );
51}

Customization ideas

Add appointment reminders via SMS

Create a scheduled Supabase Edge Function that runs daily, finds appointments scheduled for tomorrow, and sends SMS reminders to patients using Twilio. Store the Twilio credentials in Secrets and patient phone numbers in the patients table.

Add SOAP notes for clinical documentation

Replace the simple Notes tab with structured SOAP notes (Subjective, Objective, Assessment, Plan). Each note has four separate text areas and is signed with the provider's name and timestamp. Use a soap_notes table with the four sections as columns.

Build a medication interaction checker

When adding a new prescription, call a drug interaction API (like OpenFDA) via an Edge Function with the new medication and all active medications for that patient. Display any detected interactions as a warning before saving.

Add lab result tracking

Create a lab_results table storing test name, value, reference range, and date. Show a trending mini-chart (Recharts) on the patient chart for key metrics like blood glucose or cholesterol over time.

Enable telehealth appointment links

Add a video_link column to appointments. When scheduling a telehealth appointment, generate a unique meeting link (using a service like Daily.co or Jitsi) via Edge Function and store it. Both provider and patient see the link in their appointment details.

Build a referral system

Add a referrals table where providers can refer patients to specialists. Include a Select of other providers in the system, reason for referral, and priority. The referred-to provider gets a notification and the patient is added to their provider_patients list.

Common pitfalls

Pitfall: Using real patient data before completing HIPAA compliance review

How to avoid: For demonstration or testing, use only fictional data. For production with real patients, consult a healthcare compliance specialist, sign BAAs with all data processors, and implement additional technical safeguards.

Pitfall: Storing Supabase Storage signed URLs in the database

How to avoid: Generate signed URLs on demand only when a user clicks to download. Never cache or store signed URLs.

Pitfall: Not testing RLS policies by impersonating different user roles

How to avoid: In the Supabase SQL Editor, use SET LOCAL request.jwt.claims TO to impersonate different users. Create test accounts for each role and verify in the Supabase Dashboard table viewer what each user can see.

Pitfall: Allowing patients to upload documents to their own record

How to avoid: Only allow providers and admins to upload to medical_documents. The RLS INSERT policy already enforces this via is_my_patient(patient_id) — ensure this policy is in place.

Pitfall: Skipping audit logging for performance

How to avoid: Log every patient record access. Use an Edge Function for logging to include server-side metadata. Accept that audit logging adds a small latency overhead.

Best practices

  • Always display a HIPAA disclaimer on every patient-facing page, especially when using this for real clinical data
  • Use SECURITY DEFINER functions for RLS helper functions to prevent policy evaluation loops and keep policies readable
  • Generate Supabase Storage signed URLs with the shortest practical expiry (15 minutes for medical documents)
  • Audit log every patient chart view, not just data modifications — access logs are as important as change logs
  • Keep provider and patient views completely separate routes (/provider/* and /patient/*) with separate route guards
  • Never display full date of birth on list views — show age instead, and only show full DOB in the patient chart
  • Mark all allergy information prominently with red badges and warning icons across the entire application
  • Test the RLS policies by creating test accounts for each role and verifying data isolation in the Supabase Table Editor

AI prompts to try

Copy these prompts to build this project faster.

ChatGPT Prompt

I'm building a medical records app with Supabase. I have three user roles: patient, provider, and admin. Providers should only see patients they are assigned to via a provider_patients junction table. Patients should only see their own records. Admins see everything. Write complete Supabase Row Level Security policies for these tables: patients, appointments, prescriptions, medical_documents. Also write the SQL helper functions that the policies should call to avoid repeating complex joins in every policy.

Lovable Prompt

Add audit logging to the medical records app. Every time a provider views a patient chart, opens a prescription record, or downloads a document, insert a record into the audit_logs table with the user_id, action name, table name, record_id, and timestamp. Implement this as a reusable useAuditLog hook that components can call.

Build Prompt

In Lovable, set up a private Supabase Storage bucket called 'medical-records' for the medical records app. Configure the bucket Storage policy so that providers can upload files to paths starting with the patient_id of their assigned patients, and both providers and the patient themselves can read files. Create the Supabase Storage bucket policies in the Supabase Dashboard. Then in Lovable, implement a download function that generates a signed URL expiring in 15 minutes using supabase.storage.from('medical-records').createSignedUrl(path, 900).

Frequently asked questions

Is this app HIPAA-compliant for real patient data?

No — not without significant additional work. HIPAA compliance requires signed Business Associate Agreements (BAAs) with all data processors (Supabase and Lovable do not offer BAAs on standard plans), additional technical safeguards, workforce training, and a full risk assessment. This build is a demonstration of the architecture patterns. For real patient data, consult a healthcare compliance specialist before deployment.

How do I test that providers can only see their assigned patients?

Create two provider accounts and two patient accounts. Assign Patient A to Provider 1 and Patient B to Provider 2 using the provider_patients table. Log in as Provider 1 and verify you can only see Patient A in the patient list. Log in as Provider 2 and verify you see only Patient B. In the Supabase SQL Editor, you can run queries as specific users using the SET LOCAL request.jwt.claims approach to verify RLS without logging in.

Can patients see their prescriptions in this app?

Yes — the patient portal shows active prescriptions in read-only mode. The RLS policy on prescriptions allows SELECT where patient_id = my_patient_id() (the current user's patient record). Patients cannot add or modify prescriptions — only providers with an is_my_patient() relationship can INSERT or UPDATE.

How should I handle a provider who needs to cover for another provider's patients?

Add a temporary row in provider_patients for the covering provider with the same patient_id. When the coverage period ends, delete that row. You could add a start_date and end_date to provider_patients and update the is_my_patient() function to check date ranges for temporary assignments.

How do I deploy this app for a real clinic?

Before deploying for real clinical use: (1) upgrade to Supabase Pro or higher and request a BAA, (2) contact Lovable about enterprise compliance plans, (3) engage a HIPAA compliance consultant, (4) implement additional security controls like MFA for all staff accounts, (5) conduct a penetration test. This is not a quick checklist — allow 2-3 months for proper compliance work.

Why does document download use a 15-minute expiry instead of longer?

Medical documents are sensitive. A 15-minute signed URL limits the window during which a shared or stolen link could be used to access the file. If a user needs to view a document multiple times, each view generates a fresh URL. This is a security trade-off: more friction for legitimate users in exchange for reduced exposure window.

Can I add custom fields for specialized medical practices?

Yes — the patients table has a medical_history TEXT column that can store arbitrary information. For more structured custom fields, add a JSONB column called custom_fields to patients and prescriptions. Lovable can generate a dynamic form renderer that reads field definitions from a configuration table.

Can RapidDev help build a HIPAA-compliant version of this app?

RapidDev can help with the technical architecture and Lovable/Supabase implementation. HIPAA compliance itself requires legal and compliance expertise beyond development scope — we'd work alongside your compliance team to ensure the technical implementation meets the requirements they define.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help building your app?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.