Use when setting up Supabase, creating database tables, writing RLS policies, configuring Auth (especially Apple Sign-In), creating storage buckets, writing Edge Functions in TypeScript/Deno, or running migrations. Triggers on "Supabase setup", "RLS policy", "Edge Function", "database schema", "storage bucket", "Apple Sign-In auth".
Provides comprehensive guidance for Supabase backend setup including database tables, RLS policies, authentication (Apple Sign-In), storage buckets, and Edge Functions in TypeScript/Deno. Triggers on specific Supabase setup tasks like creating RLS policies, Edge Functions, database schemas, storage buckets, or Apple Sign-In configuration.
/plugin marketplace add bgrober/indie-stack/plugin install superpowers@superpowers-devThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Guide for configuring Supabase backend with proper security, RLS policies, and Edge Functions.
supabase/
├── config.toml # Local dev config
├── functions/
│ └── function-name/ # Edge Function
│ ├── index.ts # Request handling
│ └── helpers.ts # Utilities
└── migrations/
├── 001_initial.sql # Initial tables + RLS
├── 002_storage.sql # Storage buckets + policies
└── 003_new_feature.sql # Incremental changes
-- Standard table structure
CREATE TABLE items (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
-- Your columns
title TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
score INTEGER,
-- Constraints
CONSTRAINT valid_status CHECK (status IN ('pending', 'success', 'failed'))
);
-- Auto-update updated_at
CREATE TRIGGER update_items_updated_at
BEFORE UPDATE ON items
FOR EACH ROW
EXECUTE FUNCTION moddatetime(updated_at);
-- Index for user queries
CREATE INDEX items_user_id_idx ON items(user_id);
CREATE INDEX items_created_at_idx ON items(created_at DESC);
-- Enable RLS (required!)
ALTER TABLE items ENABLE ROW LEVEL SECURITY;
-- Users can only see their own items
CREATE POLICY "Users can view own items"
ON items FOR SELECT
USING (auth.uid() = user_id);
-- Users can insert their own items
CREATE POLICY "Users can insert own items"
ON items FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Users can update their own items
CREATE POLICY "Users can update own items"
ON items FOR UPDATE
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
-- Users can delete their own items
CREATE POLICY "Users can delete own items"
ON items FOR DELETE
USING (auth.uid() = user_id);
-- Profiles table
CREATE TABLE profiles (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
email TEXT,
display_name TEXT,
avatar_url TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
-- Users can view and update their own profile
CREATE POLICY "Users can view own profile"
ON profiles FOR SELECT
USING (auth.uid() = id);
CREATE POLICY "Users can update own profile"
ON profiles FOR UPDATE
USING (auth.uid() = id);
-- Auto-create profile on signup
CREATE OR REPLACE FUNCTION handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO profiles (id, email)
VALUES (NEW.id, NEW.email);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW
EXECUTE FUNCTION handle_new_user();
-- Create storage bucket
INSERT INTO storage.buckets (id, name, public)
VALUES ('item-images', 'item-images', false);
-- Users can upload to their own folder
CREATE POLICY "Users can upload own images"
ON storage.objects FOR INSERT
WITH CHECK (
bucket_id = 'item-images' AND
auth.uid()::text = (storage.foldername(name))[1]
);
-- Users can view their own images
CREATE POLICY "Users can view own images"
ON storage.objects FOR SELECT
USING (
bucket_id = 'item-images' AND
auth.uid()::text = (storage.foldername(name))[1]
);
-- Users can delete their own images
CREATE POLICY "Users can delete own images"
ON storage.objects FOR DELETE
USING (
bucket_id = 'item-images' AND
auth.uid()::text = (storage.foldername(name))[1]
);
{bucket}/{user_id}/{item_id}.{ext}
Example: item-images/abc123/def456.jpg
Supabase Dashboard:
config.toml (local dev):
[auth.external.apple]
enabled = true
client_id = "your.service.id"
// Use Supabase Swift SDK
let credentials = try await supabase.auth.signInWithApple()
// supabase/functions/process-item/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
import { createClient } from "https://esm.sh/@supabase/supabase-js@2"
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
}
serve(async (req) => {
// Handle CORS preflight
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders })
}
try {
// Verify auth
const authHeader = req.headers.get('Authorization')
if (!authHeader) {
return new Response(
JSON.stringify({ error: 'Missing authorization' }),
{ status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
)
}
// Create authenticated client
const supabase = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
{ global: { headers: { Authorization: authHeader } } }
)
// Verify user
const { data: { user }, error: userError } = await supabase.auth.getUser()
if (userError || !user) {
return new Response(
JSON.stringify({ error: 'Invalid token' }),
{ status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
)
}
// Parse request
const { itemId, data } = await req.json()
// Your logic here
const result = await processItem(itemId, data)
return new Response(
JSON.stringify(result),
{ headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
)
} catch (error) {
console.error('Error:', error)
return new Response(
JSON.stringify({ error: 'Internal server error' }),
{ status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
)
}
})
// Get API key from environment
const GEMINI_API_KEY = Deno.env.get('GEMINI_API_KEY')
async function callGemini(prompt: string, imageBase64: string) {
const response = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${GEMINI_API_KEY}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contents: [{
parts: [
{ text: prompt },
{ inline_data: { mime_type: 'image/jpeg', data: imageBase64 } }
]
}],
generationConfig: {
responseMimeType: 'application/json'
}
})
}
)
if (!response.ok) {
throw new Error(`Gemini API error: ${response.status}`)
}
const result = await response.json()
return JSON.parse(result.candidates[0].content.parts[0].text)
}
# Set secret in Supabase
supabase secrets set GEMINI_API_KEY=your-key-here
# List secrets
supabase secrets list
# Create new migration
supabase migration new add_feature_x
# Edit the generated file
# supabase/migrations/YYYYMMDDHHMMSS_add_feature_x.sql
# Local development
supabase db reset # Resets and applies all migrations
# Push to remote
supabase db push
# Check status
supabase db diff
[ ] RLS enabled on ALL tables
[ ] Policies cover SELECT, INSERT, UPDATE, DELETE
[ ] Storage policies match table patterns
[ ] service_role key NEVER in client code
[ ] API keys in environment variables
[ ] Edge Functions verify auth before processing
[ ] No sensitive data in error messages
[ ] Indexes on frequently queried columns
# Start local Supabase
supabase start
# Stop local Supabase
supabase stop
# Deploy Edge Function
supabase functions deploy function-name
# Serve Edge Function locally
supabase functions serve function-name
# Generate TypeScript types
supabase gen types typescript --local > types.ts
# View logs
supabase functions logs function-name
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.