Supabase Integration Guide 🔌

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

Server rack with glowing lights in a modern data center

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 records
  • UPDATE: Modified records
  • DELETE: 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. 🐍