Overview
Aria Healthcare uses Supabase as its backend platform, providing RESTful APIs, real-time subscriptions, authentication, and file storage. All APIs are accessed through the Supabase client with TypeScript type safety.
Getting Started
Supabase Client Setup
src/integrations/supabase/client.ts
Usage in Components
import { createClient } from '@supabase/supabase-js' ;
// Environment variables
const supabaseUrl = process . env . NEXT_PUBLIC_SUPABASE_URL || process . env . VITE_SUPABASE_URL ;
const supabaseAnonKey = process . env . NEXT_PUBLIC_SUPABASE_ANON_KEY || process . env . VITE_SUPABASE_ANON_KEY ;
if ( ! supabaseUrl || ! supabaseAnonKey ) {
console . error ( 'Missing Supabase environment variables' );
}
// Create singleton client instance
export const supabase = createClient (
supabaseUrl || 'https://placeholder.supabase.co' ,
supabaseAnonKey || 'placeholder' ,
);
Environment Variables
Your Supabase project URL (e.g., https://xxxxx.supabase.co)
NEXT_PUBLIC_SUPABASE_ANON_KEY
Your Supabase anonymous/public API key for client-side usage
Alternative environment variable for Vite compatibility
Alternative environment variable for Vite compatibility
Authentication API
Sign Up
Create a new user account with email and password.
const { data , error } = await supabase . auth . signUp ({
email: '[email protected] ' ,
password: 'secure-password' ,
options: {
data: {
name: 'Dr. John Doe' ,
user_type: 'doctor' ,
},
},
});
if ( error ) {
console . error ( 'Sign up error:' , error . message );
} else {
console . log ( 'User created:' , data . user );
}
User’s password (minimum 6 characters)
Additional user metadata (e.g., name, user_type)
User object containing id, email, and metadata
Session object with access_token and refresh_token
Sign In
Authenticate an existing user.
const { data , error } = await supabase . auth . signInWithPassword ({
email: '[email protected] ' ,
password: 'secure-password' ,
});
if ( error ) {
console . error ( 'Sign in error:' , error . message );
} else {
console . log ( 'Session:' , data . session );
}
Sign Out
End the current user session.
const { error } = await supabase . auth . signOut ();
if ( error ) {
console . error ( 'Sign out error:' , error . message );
}
Get Current User
Retrieve the currently authenticated user.
const { data : { user } } = await supabase . auth . getUser ();
if ( user ) {
console . log ( 'Current user:' , user . email );
} else {
console . log ( 'No user signed in' );
}
Session Management
Handle authentication state changes.
supabase . auth . onAuthStateChange (( event , session ) => {
console . log ( 'Auth event:' , event );
switch ( event ) {
case 'SIGNED_IN' :
console . log ( 'User signed in:' , session ?. user . email );
break ;
case 'SIGNED_OUT' :
console . log ( 'User signed out' );
break ;
case 'TOKEN_REFRESHED' :
console . log ( 'Token refreshed' );
break ;
}
});
Database API
Waitlist Table
Manage waitlist entries for new users.
Insert Waitlist Entry
import { supabase } from '@/integrations/supabase/client' ;
import { customList } from 'country-codes-list' ;
async function submitWaitlist ( values : WaitlistForm ) {
// Format phone number with country code
const callingCodes = customList ( 'countryCode' , '+{countryCallingCode}' );
const callingCode = callingCodes [ values . phoneNumberCountryCode ];
const fullPhoneNumber = ` ${ callingCode } ${ values . phoneNumber } ` ;
const { data , error } = await supabase
. from ( 'waitlist' )
. insert ({
name: values . name ,
user_type: values . userType ,
user_type_other: values . userTypeOther ,
phone_number: fullPhoneNumber ,
email: values . email ,
features: values . features ,
features_other: values . featuresOther ,
source: values . source ,
source_other: values . sourceOther ,
})
. select ();
if ( error ) {
throw new Error ( error . message );
}
return data ;
}
Type of user: doctor, patient, or other
Specification if user_type is other
Phone number with country code (e.g., +91 9876543210)
Array of interested features: ai_features, records, ABHA, appointment, messaging, telehealth, other
Specification if features includes other
How they heard about Aria: search_engine, social_media, recommendation, word_of_mouth, other
Specification if source is other
Array containing the inserted waitlist entry with auto-generated id and created_at fields
Query Waitlist Entries
Select All
Filter by User Type
Pagination
const { data , error } = await supabase
. from ( 'waitlist' )
. select ( '*' );
Patient Records Table
Manage patient medical records (example schema).
Create Patient Record
const { data , error } = await supabase
. from ( 'patient_records' )
. insert ({
patient_id: 'uuid-here' ,
abha_number: '12345678901234' ,
abha_address: 'patient@abdm' ,
doctor_id: 'doctor-uuid' ,
diagnosis: 'Hypertension' ,
prescription: [
{
medication: 'Amlodipine' ,
dosage: '5mg' ,
frequency: 'Once daily' ,
duration: '30 days' ,
},
],
notes: 'Patient advised lifestyle modifications' ,
visit_date: new Date (). toISOString (),
})
. select ();
Unique identifier for the patient
ABHA address (e.g., patient@abdm)
Unique identifier for the treating doctor
Array of prescribed medications with dosage details
Additional clinical notes
Date and time of the visit
Query Patient Records
Get Patient's Records
Get Records with Doctor Info
const { data , error } = await supabase
. from ( 'patient_records' )
. select ( '*' )
. eq ( 'patient_id' , patientId )
. order ( 'visit_date' , { ascending: false });
Real-time Subscriptions
Listen to database changes in real-time.
Subscribe to New Records
React Query Integration
const channel = supabase
. channel ( 'patient_records_changes' )
. on (
'postgres_changes' ,
{
event: 'INSERT' ,
schema: 'public' ,
table: 'patient_records' ,
filter: `patient_id=eq. ${ patientId } ` ,
},
( payload ) => {
console . log ( 'New record:' , payload . new );
// Update UI with new record
}
)
. subscribe ();
// Cleanup
return () => {
supabase . removeChannel ( channel );
};
Real-time subscriptions automatically handle reconnection and are optimized for low latency.
Storage API
Upload and manage medical records, images, and documents.
Upload File
const file = event . target . files [ 0 ];
const fileExt = file . name . split ( '.' ). pop ();
const fileName = ` ${ patientId } / ${ Date . now () } . ${ fileExt } ` ;
const { data , error } = await supabase . storage
. from ( 'medical-records' )
. upload ( fileName , file , {
cacheControl: '3600' ,
upsert: false ,
});
if ( error ) {
console . error ( 'Upload error:' , error );
} else {
console . log ( 'File uploaded:' , data . path );
}
Get File URL
Get Public URL
Get Signed URL (Private)
const { data } = supabase . storage
. from ( 'medical-records' )
. getPublicUrl ( filePath );
console . log ( 'File URL:' , data . publicUrl );
Delete File
const { data , error } = await supabase . storage
. from ( 'medical-records' )
. remove ([ filePath ]);
if ( error ) {
console . error ( 'Delete error:' , error );
}
Error Handling
All Supabase operations return an error object when something goes wrong.
Error Handling Pattern
With Try-Catch
const { data , error } = await supabase
. from ( 'waitlist' )
. insert ({ name: 'Test' });
if ( error ) {
// Handle specific error codes
switch ( error . code ) {
case '23505' : // Unique violation
console . error ( 'Duplicate entry' );
break ;
case '42501' : // Insufficient privileges
console . error ( 'Permission denied' );
break ;
default :
console . error ( 'Error:' , error . message );
}
throw error ;
}
return data ;
Type Safety
Supabase client can be configured with TypeScript types for your database schema.
// Generated from Supabase CLI
export type Database = {
public : {
Tables : {
waitlist : {
Row : {
id : string ;
name : string ;
email : string ;
phone_number : string ;
user_type : 'doctor' | 'patient' | 'other' ;
features : string [];
created_at : string ;
};
Insert : Omit < Database [ 'public' ][ 'Tables' ][ 'waitlist' ][ 'Row' ], 'id' | 'created_at' >;
Update : Partial < Database [ 'public' ][ 'Tables' ][ 'waitlist' ][ 'Insert' ]>;
};
};
};
};
// Create typed client
import { createClient } from '@supabase/supabase-js' ;
import type { Database } from './database.types' ;
export const supabase = createClient < Database >(
process . env . NEXT_PUBLIC_SUPABASE_URL ! ,
process . env . NEXT_PUBLIC_SUPABASE_ANON_KEY !
);
// Now you get full type safety
const { data } = await supabase . from ( 'waitlist' ). select ( '*' );
// data is typed as Database['public']['Tables']['waitlist']['Row'][]
Always validate and sanitize user input before inserting into the database, even with TypeScript types.
Rate Limits
Supabase implements rate limiting to prevent abuse:
Authentication : 30 requests per hour per IP
Database queries : 100 requests per second
Storage uploads : 100 requests per second
Real-time connections : 500 concurrent connections per project
Contact Supabase support for increased rate limits on production applications.
Best Practices
Use React Query for Data Fetching
Automatic caching and background refetching
Optimistic updates for better UX
Error and loading state management
Implement Row-Level Security
Always enable RLS on tables with sensitive data
Use policies to restrict access based on user roles
Use Database Functions
Complex business logic in PostgreSQL functions
Better performance than client-side processing
Handle Errors Gracefully
Show user-friendly error messages
Log detailed errors for debugging
Implement retry logic for transient failures
Optimize Queries
Select only needed columns
Use indexes for frequently queried fields
Implement pagination for large datasets
React Query Best Practice
import { useQuery , useMutation , useQueryClient } from '@tanstack/react-query' ;
import { supabase } from '@/integrations/supabase/client' ;
// Fetch data with React Query
function useWaitlist () {
return useQuery ({
queryKey: [ 'waitlist' ],
queryFn : async () => {
const { data , error } = await supabase
. from ( 'waitlist' )
. select ( '*' )
. order ( 'created_at' , { ascending: false });
if ( error ) throw error ;
return data ;
},
staleTime: 5 * 60 * 1000 , // 5 minutes
});
}
// Mutate data with optimistic updates
function useAddToWaitlist () {
const queryClient = useQueryClient ();
return useMutation ({
mutationFn : async ( values : WaitlistForm ) => {
const { data , error } = await supabase
. from ( 'waitlist' )
. insert ( values )
. select ();
if ( error ) throw error ;
return data [ 0 ];
},
onSuccess : () => {
// Invalidate and refetch
queryClient . invalidateQueries ({ queryKey: [ 'waitlist' ] });
},
});
}