Skip to main content

Supabase API Architecture - No Direct Client Calls

Overviewโ€‹

This document explains how the application is architected to ensure all database operations go through the backend API, not through direct Supabase client calls from the frontend.


โœ… Architecture Patternโ€‹

Frontend (React)
โ†“
API Calls (fetch)
โ†“
Backend API (Flask/Python)
โ†“
Supabase Service
โ†“
Supabase Database

๐Ÿ”’ Security Benefitsโ€‹

  1. No Exposed Database Logic - Business logic stays on the server
  2. Token Validation - Backend verifies auth tokens before database access
  3. Centralized Access Control - All permissions checked in one place
  4. Rate Limiting - Can add rate limiting at API level
  5. Audit Logging - All database operations logged server-side
  6. SQL Injection Prevention - Backend properly sanitizes inputs

๐Ÿ“ File Structureโ€‹

Backend API Endpointsโ€‹

  • /src/routes/enrollments.py - Enrollment operations
  • /src/routes/waitlist.py - Waitlist operations
  • /src/routes/content.py - Content management
  • /src/routes/user.py - User management

Frontend Services (API Clients)โ€‹

  • /src/services/enrollments.js - Enrollment API calls
  • /src/services/waitlist.js - Waitlist API calls
  • /src/services/novaTracks.js - Track data API calls
  • /src/services/homepageContent.js - Content API calls
  • /src/services/footerContent.js - Footer content API calls
  • /src/services/marketingPages.js - Marketing pages API calls
  • /src/services/legalContent.js - Legal content API calls

Exception: Authenticationโ€‹

  • /src/context/AuthContext.jsx - ONLY place with direct Supabase auth calls
  • This is intentional - Supabase auth SDK handles sessions, OAuth, etc.
  • Auth operations (login, signup, logout) go directly to Supabase Auth service
  • Database operations still go through backend API

๐Ÿšซ What to Avoidโ€‹

โŒ BAD - Direct Supabase Client Callsโ€‹

// DON'T DO THIS
import { supabase } from '../lib/supabaseClient';

const fetchData = async () => {
const { data } = await supabase
.from('course_enrollments')
.select('*')
.eq('user_id', userId);

return data;
};

โœ… GOOD - API Endpoint Callsโ€‹

// DO THIS INSTEAD
const fetchData = async () => {
const token = await getAuthToken();

const response = await fetch('/api/enrollments/dashboard', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
});

const data = await response.json();
return data;
};

๐Ÿ“‹ Implementation Checklistโ€‹

For Each New Feature:โ€‹

Backend (Python/Flask)โ€‹

  • Create route in /src/routes/[feature].py
  • Add authentication check (verify token)
  • Use SupabaseService for database operations
  • Add proper error handling
  • Register blueprint in main.py

Frontend (React/JavaScript)โ€‹

  • Create service file in /src/services/[feature].js
  • Use fetch() to call backend API
  • Include auth token in headers
  • Handle errors properly
  • NO direct supabase.from() calls

๐Ÿ” How to Audit for Direct Callsโ€‹

Search for Direct Supabase Usageโ€‹

# Find all direct Supabase client imports
grep -r "from.*supabaseClient" src/

# Find direct database calls
grep -r "supabase\.from(" src/

# Find direct RPC calls
grep -r "supabase\.rpc(" src/

Expected Resultsโ€‹

  • โœ… src/context/AuthContext.jsx - Auth operations only
  • โœ… src/lib/supabaseClient.js - Client initialization only
  • โŒ No other files should import or use supabase client

๐Ÿ“ Current Implementationโ€‹

Enrollmentsโ€‹

โœ… Backend: /src/routes/enrollments.py

  • GET /api/enrollments/dashboard - Get user dashboard
  • GET /api/enrollments/track/:id - Get enrollment for track
  • POST /api/enrollments - Enroll in track

โœ… Frontend: /src/services/enrollments.js

  • All calls go through API endpoints
  • Auth token included in headers

Waitlistโ€‹

โœ… Backend: /src/routes/waitlist.py

  • GET /api/waitlist/check - Check waitlist status
  • POST /api/waitlist - Join waitlist
  • DELETE /api/waitlist - Remove from waitlist

โœ… Frontend: /src/services/waitlist.js

  • All calls go through API endpoints

Contentโ€‹

โœ… Backend: /src/routes/content.py

  • All content operations

โœ… Frontend: Multiple service files

  • /src/services/homepageContent.js
  • /src/services/footerContent.js
  • /src/services/marketingPages.js
  • /src/services/legalContent.js

Tracksโ€‹

โœ… Backend: /src/routes/content.py (serves track data) โœ… Frontend: /src/services/novaTracks.js


๐Ÿ” Authentication Flowโ€‹

1. User logs in via AuthContext
โ†“
2. Supabase Auth creates session
โ†“
3. Session token stored in browser
โ†“
4. Frontend gets token from session
โ†“
5. Frontend includes token in API requests
โ†“
6. Backend verifies token with Supabase Auth
โ†“
7. Backend performs database operations
โ†“
8. Backend returns data to frontend

Code Exampleโ€‹

Frontend (Getting Token)

import { supabase } from '../lib/supabaseClient';

const getAuthToken = async () => {
const { data: { session } } = await supabase.auth.getSession();
if (!session?.access_token) {
throw new Error('Not authenticated');
}
return session.access_token;
};

Backend (Verifying Token)

auth_header = request.headers.get('Authorization')
token = auth_header.replace('Bearer ', '')

user_response = supabase_service.client.auth.get_user(token)
user_id = user_response.user.id

# Now use user_id for database operations

๐Ÿงช Testingโ€‹

Verify No Direct Callsโ€‹

# Run this in project root
npm run check-supabase-usage

Or manually:

# Should only return AuthContext and supabaseClient
grep -r "supabase\." src/ --include="*.js" --include="*.jsx" | grep -v "auth\." | grep -v "supabaseClient.js"

Test API Endpointsโ€‹

# Get auth token
TOKEN="your_token_here"

# Test dashboard
curl -H "Authorization: Bearer $TOKEN" http://localhost:5001/api/enrollments/dashboard

# Test enrollment
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"trackId":"track-id","pricingTier":"free"}' \
http://localhost:5001/api/enrollments

๐Ÿ“š Benefits Summaryโ€‹

AspectDirect SupabaseThrough API
SecurityโŒ Exposed logicโœ… Hidden logic
ScalabilityโŒ Hard to changeโœ… Easy to modify
MonitoringโŒ Client-side onlyโœ… Server logging
Rate LimitingโŒ Limitedโœ… Full control
CachingโŒ Browser onlyโœ… Server & CDN
ValidationโŒ Client-sideโœ… Server-side

๐Ÿš€ Migration Strategyโ€‹

If you find direct Supabase calls:

  1. Identify the operation - What database table/operation?
  2. Create backend route - Add to appropriate /src/routes/ file
  3. Update frontend service - Change to API call
  4. Test thoroughly - Ensure same functionality
  5. Remove direct call - Delete old Supabase client code

๐Ÿ“ž Questions?โ€‹

If you need to add new database operations:

  1. Start with backend API endpoint
  2. Add authentication check
  3. Implement database logic
  4. Create frontend service function
  5. Test end-to-end

Never start by adding supabase.from() calls in the frontend!


โœ… Verification Checklistโ€‹

  • All /src/services/*.js files use fetch() not supabase.from()
  • Only AuthContext.jsx uses supabase.auth.*
  • All database operations have backend API endpoints
  • All API calls include Authorization header
  • Backend verifies tokens before database access
  • No RLS policies bypassed
  • Error handling implemented end-to-end

Last Updated: October 2025 Architecture Status: โœ… All database calls through API