Skip to content
This repository was archived by the owner on Dec 14, 2025. It is now read-only.

Latest commit

Β 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
Β 
Β 

README.md

πŸ”Œ API Documentation

Comprehensive API documentation for the Elluminar platform, covering authentication, data access, and integration patterns.

🎯 API Overview

Elluminar uses Supabase as the backend-as-a-service, providing:

  • Auto-generated REST API from PostgreSQL schema
  • Real-time subscriptions via WebSockets
  • GraphQL endpoint for flexible queries
  • Authentication & authorization with Row Level Security
  • File storage & CDN for media assets

πŸ—οΈ API Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 β”‚    β”‚                  β”‚    β”‚                 β”‚
β”‚ Flutter Client  │◄──►│   Supabase       │◄──►│   PostgreSQL    β”‚
β”‚                 β”‚    β”‚   API Gateway    β”‚    β”‚   Database      β”‚
β”‚  β€’ HTTP Client  β”‚    β”‚                  β”‚    β”‚                 β”‚
β”‚  β€’ WebSocket    β”‚    β”‚  β€’ REST API      β”‚    β”‚  β€’ Tables       β”‚
β”‚  β€’ Auth Token   β”‚    β”‚  β€’ GraphQL       β”‚    β”‚  β€’ Views        β”‚
β”‚  β€’ Real-time    β”‚    β”‚  β€’ Real-time     β”‚    β”‚  β€’ Functions    β”‚
β”‚                 β”‚    β”‚  β€’ Storage       β”‚    β”‚  β€’ Triggers     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ”‘ Authentication & Authorization

API Authentication Flow

// 1. Client authenticates with Supabase
const { data, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'password'
});

// 2. JWT token automatically included in subsequent requests
const { data: profile } = await supabase
  .from('ConsultantProfile')
  .select('*')
  .eq('userId', data.user.id);

Authorization Levels

  • Anonymous: Public data access only
  • Authenticated: User-specific data based on RLS policies
  • Service Role: Full database access (server-side only)

API Keys

// Client-side (limited access)
const supabase = createClient(
  'https://your-project.supabase.co',
  'your-anon-key' // Row Level Security enforced
);

// Server-side (full access)  
const supabaseAdmin = createClient(
  'https://your-project.supabase.co',
  'your-service-role-key' // Bypasses RLS
);

πŸ“Š Core API Endpoints

Base URL

https://your-project.supabase.co/rest/v1/

Authentication Endpoints

Endpoint Method Purpose
/auth/v1/signup POST User registration
/auth/v1/signin POST Email/password login
/auth/v1/logout POST Sign out user
/auth/v1/user GET Get current user
/auth/v1/recover POST Password reset

Core Data Endpoints

Resource Endpoint Methods Description
Users /users GET, PATCH User account data
Consultant Profiles /ConsultantProfile GET, POST, PATCH Consultant information
Consultee Profiles /ConsulteeProfile GET, POST, PATCH Client information
Domains /Domain GET Expertise areas
Consultations /Consultation GET, POST, PATCH Consultation sessions
Appointments /Appointment GET, POST, PATCH, DELETE Scheduled meetings
Reviews /ConsultantReview GET, POST Feedback & ratings

πŸ’» API Usage Examples

User Profile Management

// Get current user profile
const { data: user } = await supabase.auth.getUser();

// Update user profile
const { data, error } = await supabase
  .from('users')
  .update({ 
    name: 'Updated Name',
    avatar_url: 'https://example.com/avatar.jpg'
  })
  .eq('id', user.id)
  .select();

Consultant Discovery

// Search consultants by domain
const { data: consultants, error } = await supabase
  .from('ConsultantProfile')
  .select(`
    *,
    Domain!inner(name),
    users!inner(name, avatar_url)
  `)
  .eq('Domain.name', 'Technology')
  .eq('verified', true)
  .order('averageRating', { ascending: false })
  .limit(10);

Booking Management

// Create new appointment
const { data: appointment, error } = await supabase
  .from('Appointment')
  .insert({
    consultantId: 'consultant-uuid',
    consulteeId: 'consultee-uuid',
    scheduledAt: '2024-01-15T10:00:00Z',
    duration: 60,
    type: 'VIDEO_CALL',
    status: 'SCHEDULED'
  })
  .select();

// Get user's appointments
const { data: appointments } = await supabase
  .from('Appointment')
  .select(`
    *,
    ConsultantProfile!inner(users(name)),
    ConsulteeProfile!inner(users(name))
  `)
  .or(`consultantId.eq.${userId},consulteeId.eq.${userId}`)
  .order('scheduledAt', { ascending: true });

Real-time Subscriptions

// Subscribe to appointment updates
const subscription = supabase
  .channel('appointments')
  .on(
    'postgres_changes',
    {
      event: '*',
      schema: 'public',
      table: 'Appointment',
      filter: `consulteeId=eq.${userId}`
    },
    (payload) => {
      console.log('Appointment update:', payload);
      // Update UI with new data
    }
  )
  .subscribe();

// Cleanup subscription
subscription.unsubscribe();

πŸ”„ Advanced Query Patterns

Complex Filtering

// Multi-condition search
const { data } = await supabase
  .from('ConsultantProfile')
  .select('*, Domain(*)')
  .gte('averageRating', 4.0)
  .lte('hourlyRate', 100)
  .in('Domain.name', ['Technology', 'Business'])
  .eq('verified', true)
  .textSearch('bio', 'react OR flutter', {
    type: 'websearch',
    config: 'english'
  });

Aggregation Queries

// Count consultants by domain
const { data } = await supabase
  .rpc('count_consultants_by_domain', {
    min_rating: 4.0
  });

// Custom RPC function in PostgreSQL:
/*
CREATE OR REPLACE FUNCTION count_consultants_by_domain(min_rating decimal)
RETURNS TABLE (domain_name text, consultant_count bigint)
AS $$
BEGIN
  RETURN QUERY
  SELECT d.name, COUNT(cp.id)
  FROM "Domain" d
  LEFT JOIN "ConsultantProfile" cp ON d.id = cp."domainId"
  WHERE cp."averageRating" >= min_rating
  GROUP BY d.name;
END;
$$ LANGUAGE plpgsql;
*/

Batch Operations

// Bulk insert appointments
const { data, error } = await supabase
  .from('Appointment')
  .insert([
    { consultantId: 'id1', scheduledAt: '2024-01-15T10:00:00Z' },
    { consultantId: 'id2', scheduledAt: '2024-01-15T11:00:00Z' },
    { consultantId: 'id3', scheduledAt: '2024-01-15T12:00:00Z' }
  ])
  .select();

πŸ“‘ Real-time Features

Channel Subscriptions

// Subscribe to table changes
const channel = supabase
  .channel('schema-db-changes')
  .on(
    'postgres_changes',
    { event: '*', schema: 'public', table: 'Appointment' },
    (payload) => console.log(payload)
  )
  .subscribe();

// Subscribe to specific row
const rowChannel = supabase
  .channel('appointment-123')
  .on(
    'postgres_changes',
    { 
      event: 'UPDATE', 
      schema: 'public', 
      table: 'Appointment',
      filter: 'id=eq.123'
    },
    handleAppointmentUpdate
  )
  .subscribe();

Presence & Broadcasting

// Track user presence
const presenceChannel = supabase.channel('consultation-room-123');

presenceChannel
  .on('presence', { event: 'sync' }, () => {
    const newState = presenceChannel.presenceState();
    console.log('Users online:', newState);
  })
  .on('presence', { event: 'join' }, ({ key, newPresences }) => {
    console.log('User joined:', key, newPresences);
  })
  .on('presence', { event: 'leave' }, ({ key, leftPresences }) => {
    console.log('User left:', key, leftPresences);
  })
  .subscribe(async (status) => {
    if (status === 'SUBSCRIBED') {
      await presenceChannel.track({
        userId: user.id,
        userName: user.name,
        onlineAt: new Date().toISOString(),
      });
    }
  });

πŸ’Ύ File Storage API

Upload Files

// Upload user avatar
const { data, error } = await supabase.storage
  .from('avatars')
  .upload(`${user.id}/avatar.jpg`, file, {
    upsert: true,
    contentType: 'image/jpeg'
  });

// Get public URL
const { data: { publicUrl } } = supabase.storage
  .from('avatars')
  .getPublicUrl(`${user.id}/avatar.jpg`);

File Management

// List files in bucket
const { data: files } = await supabase.storage
  .from('consultation-docs')
  .list(`${consultationId}/`, {
    limit: 100,
    offset: 0
  });

// Delete file
const { error } = await supabase.storage
  .from('consultation-docs')
  .remove([`${consultationId}/document.pdf`]);

πŸ” Security Best Practices

Row Level Security (RLS)

-- Example RLS policy for ConsultantProfile
CREATE POLICY "Users can view all verified consultant profiles"
ON "ConsultantProfile"
FOR SELECT
TO authenticated
USING (verified = true);

CREATE POLICY "Consultants can update own profile"  
ON "ConsultantProfile"
FOR UPDATE
TO authenticated
USING (userId = auth.uid())
WITH CHECK (userId = auth.uid());

API Security Checklist

  • Never expose service role key in client-side code
  • Validate user input before database operations
  • Use parameterized queries to prevent SQL injection
  • Implement rate limiting for sensitive operations
  • Audit sensitive operations with triggers/functions
  • Use HTTPS everywhere for data in transit
  • Regularly rotate API keys and tokens

πŸ“Š Error Handling

Common Error Patterns

// Standardized error handling
async function handleApiCall<T>(
  apiCall: () => Promise<{ data: T | null; error: any }>
): Promise<T> {
  try {
    const { data, error } = await apiCall();
    
    if (error) {
      // Log error for monitoring
      console.error('API Error:', error);
      
      // Handle specific error types
      switch (error.code) {
        case 'PGRST116': // Row not found
          throw new Error('Resource not found');
        case '42501': // RLS violation  
          throw new Error('Access denied');
        case '23505': // Unique violation
          throw new Error('Resource already exists');
        default:
          throw new Error(error.message || 'Unknown error');
      }
    }
    
    return data!;
  } catch (error) {
    // Network or other errors
    if (error instanceof TypeError) {
      throw new Error('Network error - check connection');
    }
    throw error;
  }
}

πŸ§ͺ Testing APIs

Unit Testing

// Mock Supabase client for testing
const mockSupabase = {
  from: jest.fn(() => ({
    select: jest.fn().mockResolvedValue({ 
      data: mockConsultants, 
      error: null 
    }),
    insert: jest.fn().mockResolvedValue({ 
      data: mockAppointment, 
      error: null 
    })
  }))
};

// Test API service
test('should fetch consultants', async () => {
  const consultants = await getConsultantsByDomain('Technology');
  expect(consultants).toHaveLength(3);
  expect(consultants[0]).toHaveProperty('verified', true);
});

Integration Testing

// Test with real Supabase instance
const testSupabase = createClient(
  process.env.TEST_SUPABASE_URL!,
  process.env.TEST_SUPABASE_ANON_KEY!
);

beforeEach(async () => {
  // Clean test data
  await testSupabase.from('TestData').delete().neq('id', '');
});

πŸ“š Additional Resources

Official Documentation

SDK References

Tools & Extensions


πŸ”— Need help with API integration? Check the Getting Started Guide or Troubleshooting!