Supabase Integration Guide 🔌
Master the backend that powers CrittrHavens. A comprehensive guide to Supabase integration for developers new to the platform.

What is Supabase?
Your open-source Firebase alternative with a PostgreSQL heart.
Supabase Overview
Core Components:
- PostgreSQL Database: Full-featured relational database
- Authentication: User management and JWT tokens
- Real-time Subscriptions: Live data updates
- Storage: File and media management
- Edge Functions: Serverless compute
- Vector Embeddings: AI/ML capabilities
Why Supabase for CrittrHavens?
Perfect Fit:
- Real-time care updates
- Secure user authentication
- Photo storage for crittrs
- Row-level data security
- Open-source flexibility
- PostgreSQL power
Database Schema Overview
Understanding CrittrHavens' data structure.
Core Tables
Primary Entities:
-- User profiles
profiles (
user_id UUID PRIMARY KEY,
display_name TEXT,
subscription_status TEXT,
created_at TIMESTAMP
)
-- Reptile havens
havens (
id UUID PRIMARY KEY,
user_id UUID REFERENCES profiles,
name TEXT NOT NULL,
slot_count INTEGER,
layout_type TEXT
)
-- Individual crittrs
crittrs (
id UUID PRIMARY KEY,
haven_id UUID REFERENCES havens,
name TEXT NOT NULL,
species TEXT,
morph TEXT,
photos JSONB[]
)
-- Care logs
logs (
id UUID PRIMARY KEY,
crittr_id UUID REFERENCES crittrs,
log_type TEXT,
content TEXT,
measurements JSONB,
created_at TIMESTAMP
)
Relationships
profiles (1) → (∞) havens
havens (1) → (∞) crittrs
crittrs (1) → (∞) logs
crittrs (1) → (∞) tasks
Data Types
Custom Types:
- UUID for all IDs
- JSONB for flexible data
- TIMESTAMP with timezone
- TEXT for user content
- INTEGER for counts
Authentication Flow
Secure user management from signup to session.
Authentication Setup
Client Configuration:
// Initialize Supabase client with auth
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.VITE_SUPABASE_URL,
process.env.VITE_SUPABASE_ANON_KEY,
{
auth: {
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: true
}
}
);
Sign Up Flow
User Registration:
// Sign up new user
const { data, error } = await supabase.auth.signUp({
email: 'keeper@reptiles.com',
password: 'SecurePassword123!',
options: {
data: {
display_name: 'Reptile Keeper'
}
}
});
// Email confirmation disabled in config
// User can sign in immediately
Sign In Methods
Available Options:
// Email/Password
await supabase.auth.signInWithPassword({
email, password
});
// OAuth Providers
await supabase.auth.signInWithOAuth({
provider: 'google'
});
// Magic Link (coming soon)
await supabase.auth.signInWithOtp({
email
});
Session Management
Token Handling:
// Get current session
const { data: { session } } = await supabase.auth.getSession();
// Listen for auth changes
supabase.auth.onAuthStateChange((event, session) => {
if (event === 'SIGNED_IN') {
// Handle sign in
} else if (event === 'SIGNED_OUT') {
// Handle sign out
}
});
// Refresh token
await supabase.auth.refreshSession();
Row Level Security (RLS)
Database-level security ensuring users only access their data.
RLS Concepts
Security at the Database:
- Policies run on every query
- User context from JWT
- Granular permissions
- Zero trust architecture
Policy Examples
User Data Isolation:
-- Users can only see their own havens
CREATE POLICY "Users view own havens"
ON havens FOR SELECT
USING (auth.uid() = user_id);
-- Users can only modify their own crittrs
CREATE POLICY "Users manage own crittrs"
ON crittrs FOR ALL
USING (
haven_id IN (
SELECT id FROM havens
WHERE user_id = auth.uid()
)
);
Policy Patterns
Common Patterns:
-- Public read
USING (true)
-- Owner only
USING (auth.uid() = user_id)
-- Subscription check
USING (
EXISTS (
SELECT 1 FROM profiles
WHERE user_id = auth.uid()
AND subscription_status IN ('care', 'care_plus')
)
)
Real-time Subscriptions
Live updates for collaborative features.
Setting Up Subscriptions
Basic Subscription:
// Subscribe to haven changes
const subscription = supabase
.channel('haven-changes')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'havens',
filter: `user_id=eq.${userId}`
},
(payload) => {
console.log('Change:', payload);
// Update UI
}
)
.subscribe();
Subscription Events
Event Types:
INSERT: New recordsUPDATE: Modified recordsDELETE: Removed records*: All changes
Managing Subscriptions
Cleanup:
// Unsubscribe when done
subscription.unsubscribe();
// Remove all subscriptions
supabase.removeAllChannels();
Edge Functions
Serverless functions for complex operations.
Function Structure
Basic Edge Function:
// supabase/functions/generate-report/index.ts
import { serve } from 'https://deno.land/std/http/server.ts';
serve(async (req) => {
const { crittr_id } = await req.json();
// Generate report logic
const report = await generateVetReport(crittr_id);
return new Response(
JSON.stringify(report),
{ headers: { 'Content-Type': 'application/json' } }
);
});
Invoking Functions
Client Call:
// Call edge function
const { data, error } = await supabase.functions.invoke(
'generate-report',
{
body: { crittr_id: '123' }
}
);
Storage Configuration
Managing photos and files.
Storage Buckets
Bucket Setup:
// Upload photo
const { data, error } = await supabase.storage
.from('crittr-photos')
.upload(`${userId}/${fileName}`, file, {
cacheControl: '3600',
upsert: false
});
// Get public URL
const { data: { publicUrl } } = supabase.storage
.from('crittr-photos')
.getPublicUrl(filePath);
Storage Policies
Access Control:
-- Users can upload to their folder
CREATE POLICY "User uploads"
ON storage.objects FOR INSERT
WITH CHECK (
bucket_id = 'crittr-photos' AND
auth.uid()::text = (storage.foldername(name))[1]
);
Environment Setup
Configuring your development environment.
Required Variables
.env.local:
# Supabase Configuration
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-key
VITE_SUPABASE_SERVICE_KEY=your-service-key # Server only!
Local Development
Supabase CLI:
# Install CLI
npm install -g supabase
# Initialize project
supabase init
# Start local instance
supabase start
# Run migrations
supabase db push
Common Operations
Frequently used Supabase patterns.
CRUD Operations
Create:
const { data, error } = await supabase
.from('crittrs')
.insert({ name, species, haven_id })
.select()
.single();
Read:
const { data, error } = await supabase
.from('crittrs')
.select('*, havens(*)')
.eq('haven_id', havenId)
.order('created_at', { ascending: false });
Update:
const { data, error } = await supabase
.from('crittrs')
.update({ name, morph })
.eq('id', crittrId)
.select()
.single();
Delete:
const { error } = await supabase
.from('crittrs')
.delete()
.eq('id', crittrId);
Advanced Queries
Joins & Relations:
// Nested select
const { data } = await supabase
.from('havens')
.select(`
*,
crittrs (
*,
logs (*)
)
`)
.eq('user_id', userId);
Aggregations:
// Count records
const { count } = await supabase
.from('logs')
.select('*', { count: 'exact', head: true })
.eq('crittr_id', crittrId);
Filters:
// Complex filtering
const { data } = await supabase
.from('logs')
.select('*')
.eq('log_type', 'feeding')
.gte('created_at', startDate)
.lte('created_at', endDate)
.order('created_at', { ascending: false })
.limit(10);
Debugging Supabase
Troubleshooting common issues.
Debug Tools
Logging Queries:
// Enable debug mode
const supabase = createClient(url, key, {
db: { schema: 'public' },
global: { headers: { 'x-debug': 'true' } }
});
Common Issues
RLS Errors:
// Check if RLS is blocking
// Error: "new row violates row-level security policy"
// Solution: Review policies, check auth.uid()
Auth Issues:
// Session expired
if (error?.message?.includes('JWT')) {
await supabase.auth.refreshSession();
}
Query Errors:
// Handle errors gracefully
const { data, error } = await supabase
.from('crittrs')
.select('*');
if (error) {
console.error('Query failed:', error.message);
// Show user-friendly message
}
Performance Tips
Optimization:
- Use
select()to limit columns - Add database indexes
- Paginate large datasets
- Cache frequently accessed data
- Use connection pooling
Security Best Practices
Keeping your Supabase integration secure.
API Key Management
Key Types:
- Anon Key: Client-side, public
- Service Key: Server-side only
- JWT Secret: Never expose
Security Checklist
- ✅ Enable RLS on all tables
- ✅ Validate input on edge functions
- ✅ Use environment variables
- ✅ Implement rate limiting
- ✅ Audit security policies
- ✅ Monitor for anomalies
Testing Supabase
Testing strategies for Supabase integration.
Unit Testing
Mock Supabase:
// Mock client for tests
const mockSupabase = {
from: jest.fn(() => ({
select: jest.fn(() => ({
eq: jest.fn(() => Promise.resolve({ data: [], error: null }))
}))
}))
};
Integration Testing
Test Database:
// Use test database
const testSupabase = createClient(
process.env.TEST_SUPABASE_URL,
process.env.TEST_SUPABASE_KEY
);
Migration Management
Database schema evolution.
Creating Migrations
# Generate migration
supabase migration new add_species_table
# Edit migration file
# supabase/migrations/[timestamp]_add_species_table.sql
Migration Example
-- Create new table
CREATE TABLE species (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
care_level TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
-- Add RLS
ALTER TABLE species ENABLE ROW LEVEL SECURITY;
-- Create policy
CREATE POLICY "Public read" ON species
FOR SELECT USING (true);
Resources & Learning
Continue your Supabase journey.
Official Resources
CrittrHavens Specific
- Database schema:
/supabase/migrations/ - Type definitions:
/src/integrations/supabase/types.ts - Client setup:
/src/integrations/supabase/client.ts
Next Steps
Deepen your backend knowledge.
Building robust reptile care management with the power of PostgreSQL and the simplicity of Supabase. 🐍