Implements Supabase PostgreSQL database with JavaScript client, Row Level Security, real-time subscriptions, and edge functions. Use when building apps with Supabase, implementing RLS policies, or needing real-time database features.
Implements Supabase PostgreSQL database operations with JavaScript client. Use when building apps with Supabase, implementing Row Level Security policies, or needing real-time database subscriptions and edge functions.
/plugin marketplace add mgd34msu/goodvibes-plugin/plugin install goodvibes@goodvibes-marketThis skill inherits all available tools. When active, it can use any tool Claude has access to.
references/realtime.mdreferences/rls-patterns.mdSupabase provides a full PostgreSQL database with automatic REST/GraphQL APIs, real-time subscriptions, Row Level Security, and edge functions.
npm install @supabase/supabase-js
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!
)
// Select all columns
const { data, error } = await supabase
.from('users')
.select('*')
// Select specific columns
const { data, error } = await supabase
.from('users')
.select('id, name, email')
// Select with relations
const { data, error } = await supabase
.from('posts')
.select(`
id,
title,
author:users(name, email),
comments(id, body)
`)
// Filtering
const { data, error } = await supabase
.from('users')
.select('*')
.eq('status', 'active')
.gt('age', 18)
.order('created_at', { ascending: false })
.limit(10)
// Single record
const { data, error } = await supabase
.from('users')
.select('*')
.eq('id', userId)
.single()
// Insert single record
const { data, error } = await supabase
.from('users')
.insert({
name: 'John Doe',
email: 'john@example.com'
})
.select()
// Insert multiple records
const { data, error } = await supabase
.from('users')
.insert([
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' }
])
.select()
// Upsert (insert or update)
const { data, error } = await supabase
.from('users')
.upsert({
id: 1,
name: 'Updated Name',
email: 'updated@example.com'
})
.select()
// Update by condition
const { data, error } = await supabase
.from('users')
.update({ status: 'inactive' })
.eq('id', userId)
.select()
// Update multiple records
const { data, error } = await supabase
.from('users')
.update({ verified: true })
.in('id', [1, 2, 3])
.select()
const { error } = await supabase
.from('users')
.delete()
.eq('id', userId)
// Soft delete pattern
const { error } = await supabase
.from('users')
.update({ deleted_at: new Date().toISOString() })
.eq('id', userId)
RLS enforces authorization at the database level. Always enable RLS on public tables.
-- Enable RLS on table
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
-- Force RLS for table owner too
ALTER TABLE users FORCE ROW LEVEL SECURITY;
-- Public read access
CREATE POLICY "Public profiles visible"
ON profiles FOR SELECT
TO anon, authenticated
USING (true);
-- Users can only read their own data
CREATE POLICY "Users read own data"
ON users FOR SELECT
TO authenticated
USING ((SELECT auth.uid()) = id);
-- Users can insert their own data
CREATE POLICY "Users create own profile"
ON profiles FOR INSERT
TO authenticated
WITH CHECK ((SELECT auth.uid()) = user_id);
-- Users can update their own data
CREATE POLICY "Users update own data"
ON profiles FOR UPDATE
TO authenticated
USING ((SELECT auth.uid()) = user_id)
WITH CHECK ((SELECT auth.uid()) = user_id);
-- Users can delete their own data
CREATE POLICY "Users delete own data"
ON profiles FOR DELETE
TO authenticated
USING ((SELECT auth.uid()) = user_id);
-- Role-based access using JWT claims
CREATE POLICY "Admins have full access"
ON users FOR ALL
TO authenticated
USING (
(SELECT auth.jwt() ->> 'role') = 'admin'
);
-- Team-based access
CREATE POLICY "Team members access"
ON projects FOR SELECT
TO authenticated
USING (
team_id IN (
SELECT team_id FROM team_members
WHERE user_id = (SELECT auth.uid())
)
);
-- GOOD: Wrap auth functions in SELECT for caching
USING ((SELECT auth.uid()) = user_id)
-- BAD: Direct function call
USING (auth.uid() = user_id)
-- Add indexes on columns used in policies
CREATE INDEX idx_profiles_user_id ON profiles(user_id);
// Subscribe to all changes
const channel = supabase
.channel('table-changes')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'messages'
},
(payload) => {
console.log('Change:', payload)
}
)
.subscribe()
// Subscribe to specific events
const channel = supabase
.channel('new-messages')
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'messages',
filter: 'room_id=eq.123'
},
(payload) => {
console.log('New message:', payload.new)
}
)
.subscribe()
// Broadcast messages (no database)
const channel = supabase.channel('room-1')
channel.on('broadcast', { event: 'cursor' }, (payload) => {
console.log('Cursor:', payload)
})
await channel.subscribe()
// Send broadcast
channel.send({
type: 'broadcast',
event: 'cursor',
payload: { x: 100, y: 200 }
})
// Cleanup
supabase.removeChannel(channel)
// Generate types from database
// npx supabase gen types typescript --project-id your-project > types/supabase.ts
import { Database } from './types/supabase'
const supabase = createClient<Database>(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!
)
// Typed queries
type User = Database['public']['Tables']['users']['Row']
type InsertUser = Database['public']['Tables']['users']['Insert']
type UpdateUser = Database['public']['Tables']['users']['Update']
const { data } = await supabase
.from('users')
.select('*')
.returns<User[]>()
-- Create a function
CREATE OR REPLACE FUNCTION get_user_stats(user_id UUID)
RETURNS JSON AS $$
SELECT json_build_object(
'post_count', (SELECT COUNT(*) FROM posts WHERE author_id = user_id),
'follower_count', (SELECT COUNT(*) FROM follows WHERE following_id = user_id)
);
$$ LANGUAGE SQL SECURITY DEFINER;
// Call the function
const { data, error } = await supabase
.rpc('get_user_stats', { user_id: '123' })
// Full-text search
const { data } = await supabase
.from('posts')
.select('*')
.textSearch('title', 'serverless database')
// Range queries
const { data } = await supabase
.from('events')
.select('*')
.gte('start_date', '2024-01-01')
.lte('end_date', '2024-12-31')
// Pattern matching
const { data } = await supabase
.from('users')
.select('*')
.ilike('name', '%john%')
// JSON queries
const { data } = await supabase
.from('users')
.select('*')
.contains('metadata', { role: 'admin' })
// Count
const { count } = await supabase
.from('users')
.select('*', { count: 'exact', head: true })
// Use service role key for admin operations (bypasses RLS)
import { createClient } from '@supabase/supabase-js'
const supabaseAdmin = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY! // Keep secret!
)
// Admin operations bypass RLS
const { data } = await supabaseAdmin
.from('users')
.select('*')
const { data, error } = await supabase
.from('users')
.select('*')
if (error) {
if (error.code === 'PGRST116') {
// No rows found
} else if (error.code === '42501') {
// RLS policy violation
} else {
console.error('Database error:', error.message)
}
return
}
// data is available here
const PAGE_SIZE = 10
async function getPage(page: number) {
const from = page * PAGE_SIZE
const to = from + PAGE_SIZE - 1
const { data, error, count } = await supabase
.from('posts')
.select('*', { count: 'exact' })
.order('created_at', { ascending: false })
.range(from, to)
return {
data,
totalPages: Math.ceil((count || 0) / PAGE_SIZE)
}
}
async function getNextPage(cursor?: string) {
let query = supabase
.from('posts')
.select('*')
.order('created_at', { ascending: false })
.limit(10)
if (cursor) {
query = query.lt('created_at', cursor)
}
const { data } = await query
const nextCursor = data?.[data.length - 1]?.created_at
return { data, nextCursor }
}
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 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 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.