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โ
- No Exposed Database Logic - Business logic stays on the server
- Token Validation - Backend verifies auth tokens before database access
- Centralized Access Control - All permissions checked in one place
- Rate Limiting - Can add rate limiting at API level
- Audit Logging - All database operations logged server-side
- 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
SupabaseServicefor 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 dashboardGET /api/enrollments/track/:id- Get enrollment for trackPOST /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 statusPOST /api/waitlist- Join waitlistDELETE /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โ
| Aspect | Direct Supabase | Through 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:
- Identify the operation - What database table/operation?
- Create backend route - Add to appropriate
/src/routes/file - Update frontend service - Change to API call
- Test thoroughly - Ensure same functionality
- Remove direct call - Delete old Supabase client code
๐ Questions?โ
If you need to add new database operations:
- Start with backend API endpoint
- Add authentication check
- Implement database logic
- Create frontend service function
- Test end-to-end
Never start by adding supabase.from() calls in the frontend!
โ Verification Checklistโ
- All
/src/services/*.jsfiles usefetch()notsupabase.from() - Only
AuthContext.jsxusessupabase.auth.* - All database operations have backend API endpoints
- All API calls include
Authorizationheader - 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