supabase

from alinaqi/claude-bootstrap

Opinionated project initialization for Claude Code. Security-first, spec-driven, AI-native.

448 stars37 forksUpdated Jan 20, 2026
npx skills add https://github.com/alinaqi/claude-bootstrap --skill supabase

SKILL.md

Supabase Core Skill

Load with: base.md + [supabase-nextjs.md | supabase-python.md | supabase-node.md]

Core concepts, CLI workflow, and patterns common to all Supabase projects.

Sources: Supabase Docs | Supabase CLI


Core Principle

Local-first, migrations in version control, never touch production directly.

Develop locally with the Supabase CLI, capture all changes as migrations, and deploy through CI/CD.


Supabase Stack

ServicePurpose
DatabasePostgreSQL with extensions
AuthUser authentication, OAuth providers
StorageFile storage with RLS
Edge FunctionsServerless Deno functions
RealtimeWebSocket subscriptions
VectorAI embeddings (pgvector)

CLI Setup

Install & Login

# macOS
brew install supabase/tap/supabase

# npm (alternative)
npm install -g supabase

# Login
supabase login

Initialize Project

# In your project directory
supabase init

# Creates:
# supabase/
# ├── config.toml      # Local config
# ├── seed.sql         # Seed data
# └── migrations/      # SQL migrations

Link to Remote

# Get project ref from dashboard URL: https://supabase.com/dashboard/project/<ref>
supabase link --project-ref <project-id>

# Pull existing schema
supabase db pull

Start Local Stack

supabase start

# Output:
# API URL: http://localhost:54321
# GraphQL URL: http://localhost:54321/graphql/v1
# DB URL: postgresql://postgres:postgres@localhost:54322/postgres
# Studio URL: http://localhost:54323
# Anon key: eyJ...
# Service role key: eyJ...

Migration Workflow

Option 1: Dashboard + Diff (Quick Prototyping)

# 1. Make changes in local Studio (localhost:54323)
# 2. Generate migration from diff
supabase db diff -f <migration_name>

# 3. Review generated SQL
cat supabase/migrations/*_<migration_name>.sql

# 4. Reset to test
supabase db reset

Option 2: Write Migrations Directly (Recommended)

# 1. Create empty migration
supabase migration new create_users_table

# 2. Edit the migration file
# supabase/migrations/<timestamp>_create_users_table.sql

# 3. Apply locally
supabase db reset

Option 3: ORM Migrations (Best DX)

Use Drizzle (TypeScript) or SQLAlchemy (Python) - see framework-specific skills.

Deploy Migrations

# Push to remote (staging/production)
supabase db push

# Check migration status
supabase migration list

Database Patterns

Enable RLS on All Tables

-- Always enable RLS
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;

-- Default deny - must create policies
CREATE POLICY "Users can view own profile"
  ON public.profiles
  FOR SELECT
  USING (auth.uid() = id);

Common RLS Policies

-- Public read
CREATE POLICY "Public read access"
  ON public.posts FOR SELECT
  USING (true);

-- Authenticated users only
CREATE POLICY "Authenticated users can insert"
  ON public.posts FOR INSERT
  WITH CHECK (auth.role() = 'authenticated');

-- Owner access
CREATE POLICY "Users can update own records"
  ON public.posts FOR UPDATE
  USING (auth.uid() = user_id);

-- Admin access (using custom claim)
CREATE POLICY "Admins have full access"
  ON public.posts FOR ALL
  USING (auth.jwt() ->> 'role' = 'admin');

Link to auth.users

-- Profile table linked to auth
CREATE TABLE public.profiles (
  id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
  username TEXT UNIQUE NOT NULL,
  avatar_url TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Auto-create profile on signup
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO public.profiles (id, username)
  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 public.handle_new_user();

Seed Data

supabase/seed.sql

-- Runs on `supabase db reset`
-- Use ON CONFLICT for idempotency

INSERT INTO public.profiles (id, username, avatar_url)
VALUES
  ('d0e1f2a3-b4c5-6d7e-8f9a-0b1c2d3e4f5a', 'testuser', null),
  ('a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d', 'admin', null)
ON CONFLICT (id) DO NOTHING;

Environment Variables

Required Variables

# Public (safe for client-side)
SUPABASE_URL=https://xxxxx.supabase.co
SUPABASE_ANON_KEY=eyJ...

# Private (server-side only - NEVER expose)
SUPABASE_SERVICE_ROLE_KEY=eyJ...
DATABASE_URL=postgresql://postgres.[ref]:[password]@aws-0-region.pooler.supabase.com:6543/postgres

Local vs Production

# .env.local (local development)
SUPABASE_URL=http://localhost:54321
SUPABASE_ANON_KEY=<from supabase start>
DATABASE_URL=postgresql://postgres:postgres@localhost:54322/postgres

# .env.production (remote)
SUPABASE_URL=https://xx

...
Read full content

Repository Stats

Stars448
Forks37
LicenseMIT License