supabase-schema-change
Complete workflow for Supabase schema changes. This skill should be used when modifying database schema including tables, columns, functions, views, triggers, indexes, or RLS policies. Automatically t
SKILL.md.claude/skills/supabase-schema-change/SKILL.md
GitHubSupabase Schema Change Workflow
Overview
This skill provides a complete, enforced workflow for making Supabase schema changes in any Next.js + Supabase project. It ensures all changes are properly migrated, typed, and propagated through the entire codebase.
Supported Schema Changes
| Category | Operations |
|---|---|
| Tables | CREATE, ALTER, DROP, RENAME |
| Columns | ADD, DROP, ALTER, RENAME |
| Functions | CREATE, REPLACE, DROP |
| Views | CREATE, REPLACE, DROP |
| Triggers | CREATE, DROP, ENABLE, DISABLE |
| Indexes | CREATE, DROP |
| RLS Policies | CREATE, ALTER, DROP |
| Constraints | ADD, DROP (FK, UNIQUE, CHECK) |
Critical Rules
- ALWAYS use Supabase MCP first - Query real-time database state before any changes
- NEVER skip steps - Each step depends on the previous one
- ⚠️ MANDATORY: Ask for confirmation - Present a detailed Migration Plan and wait for explicit user approval before applying ANY changes
- UPDATE all layers - Types, API routes, services, and UI components
- DOCUMENT everything - Migration files should be self-explanatory
Mandatory Confirmation Step
Before applying ANY schema change, you MUST:
Present a formatted Migration Plan showing:
- Table/Object being modified
- Action (ADD/DROP/ALTER/CREATE/etc.)
- Changes table with columns, types, and actions
- SQL to execute
- Files that need updating
- Impact assessment (breaking changes, rollback SQL)
Ask explicitly: "Proceed with migration? (yes/no)"
Only proceed if user confirms "Yes"
Example Migration Plan format:
## Migration Plan
**Table:** `public.table_name`
**Action:** DROP column
### Changes:
| Column | Type | Action |
|--------|------|--------|
| column_name | UUID | DROP |
### SQL to Execute:
ALTER TABLE public.table_name DROP COLUMN IF EXISTS column_name;
### Files to Update:
- supabase/migrations/YYYYMMDD_*.sql - Delete or update
- types/entity.ts - Remove from interfaces
- app/api/module/route.ts - Remove from GET, POST, PUT
- app/api/module/[id]/route.ts - Remove from GET
**Proceed with migration? (yes/no)**
Schema Change Decision Tree
What are you changing?
├── Column (ADD/DROP/ALTER)
│ └── Follow: Column Change Workflow
├── Function (CREATE/REPLACE/DROP)
│ └── Follow: Function Change Workflow
├── View (CREATE/REPLACE/DROP)
│ └── Follow: View Change Workflow
├── Trigger (CREATE/DROP)
│ └── Follow: Trigger Change Workflow
├── Index (CREATE/DROP)
│ └── Follow: Index Change Workflow
├── RLS Policy (CREATE/ALTER/DROP)
│ └── Follow: RLS Policy Workflow
└── Table (CREATE/DROP/RENAME)
└── Follow: Table Change Workflow
Column Change Workflow (10 Steps)
Step 1: Query Current Schema
-- Check current table structure using MCP
mcp__supabase__execute_sql
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'your_table'
ORDER BY ordinal_position;
-- For ADD: Check if column already exists
mcp__supabase__execute_sql
SELECT column_name FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'your_table'
AND column_name = 'new_column';
-- For DROP: Check dependencies
mcp__supabase__execute_sql
SELECT
tc.constraint_name,
tc.constraint_type,
kcu.column_name
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
ON tc.constraint_name = kcu.constraint_name
WHERE tc.table_name = 'your_table'
AND kcu.column_name = 'column_to_drop';
Step 2: Validate Changes
For ADD column:
- Column doesn't already exist
- Data type is appropriate
- Nullable vs NOT NULL decision made
- Default value specified if needed
- Naming follows snake_case convention
For DROP column:
- Column exists
- No foreign key dependencies
- No index dependencies
- No view dependencies
- No function dependencies
For ALTER column:
- Current data is compatible with new type
- NOT NULL constraint can be satisfied
Step 3: Create Migration Query
-- ADD column
ALTER TABLE public.table_name
ADD COLUMN IF NOT EXISTS column_name DATA_TYPE DEFAULT default_value;
-- DROP column
ALTER TABLE public.table_name
DROP COLUMN IF EXISTS column_name;
-- ALTER column type
ALTER TABLE public.table_name
ALTER COLUMN column_name TYPE NEW_TYPE USING column_name::NEW_TYPE;
-- ALTER column nullable
ALTER TABLE public.table_name
ALTER COLUMN column_name SET NOT NULL;
-- or
ALTER TABLE public.table_name
ALTER COLUMN column_name DROP NOT NULL;
-- RENAME column
ALTER TABLE public.table_name
RENAME COLUMN old_name TO new_name;
Step 4: Ask for Confirmation (MANDATORY)
⚠️ CRITICAL: NEVER skip this step. ALWAYS present the migration plan and wait for user approval.
Present a formatted migration plan and use AskUserQuestion tool to get explicit confirmation:
Migration Plan Template
## Migration Plan
**Table:** `public.table_name`
**Action:** [ADD/DROP/ALTER/RENAME] column
### Changes:
| Column | Type | Nullable | Default | Action |
|--------|------|----------|---------|--------|
| column_name | DATA_TYPE | YES/NO | value | ADD/DROP/ALTER |
### SQL to Execute:
```sql
ALTER TABLE public.table_name
[ADD COLUMN IF NOT EXISTS / DROP COLUMN IF EXISTS / ALTER COLUMN] column_name ...;
Files to Update:
| File | Change Required |
|---|---|
supabase/migrations/YYYYMMDD_*.sql |
Create/Update/Delete migration |
types/[entity].ts |
Add/Remove field from interface(s) |
app/api/[module]/route.ts |
Update SELECT, INSERT, UPDATE queries |
app/api/[module]/[id]/route.ts |
Update SELECT query |
services/[module]/* |
Check for field references |
app/[route]/[module]/page.tsx |
Check UI references |
Impact Assessment:
- Breaking Changes: [Yes/No - describe if yes]
- Data Migration Required: [Yes/No - describe if yes]
- Rollback SQL:
[rollback statement]
Proceed with migration? (yes/no)
---
#### Confirmation Request
After presenting the plan, use `AskUserQuestion` tool:
```typescript
AskUserQuestion({
questions: [{
question: "Proceed with the schema migration as described above?",
header: "Confirm",
options: [
{ label: "Yes, proceed", description: "Apply the migration and update all files" },
{ label: "No, cancel", description: "Cancel the migration - no changes will be made" }
],
multiSelect: false
}]
})
Only proceed to Step 5 if user confirms "Yes".
Step 5: Apply Migration
mcp__supabase__apply_migration
Name: [action]_[column]_[direction]_[table] (e.g., add_verified_at_to_users)
Query: [SQL statement]
Verify:
mcp__supabase__execute_sql
SELECT column_name, data_type FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'your_table';
Step 6: Update Migration File
Location: supabase/migrations/YYYYMMDD_description.sql
-- Migration: [Description]
-- Date: YYYY-MM-DD
-- Type: Column [ADD/DROP/ALTER]
-- Status: APPLIED via Supabase MCP
[SQL statement]
-- Rollback (if applicable):
-- [Rollback SQL]
Step 7: Update TypeScript Types
// types/[entity].ts
export interface Entity {
// For ADD: add the field
new_field?: string
// For DROP: remove the field
// deleted_field was here
}
Step 8: Update API Routes
For ADD: Add to SELECT, INSERT, UPDATE queries For DROP: Remove from all queries
Step 9: Update Service Files
Check if services have custom transformations that reference the column.
Step 10: Check UI Components
For ADD: Add to forms/tables if needed For DROP: Remove from all UI references
Function Change Workflow
Step 1: Query Existing Function
mcp__supabase__execute_sql
SELECT
routine_name,
routine_type,
data_type as return_type,
routine_definition
FROM information_schema.routines
WHERE routine_schema = 'public'
AND routine_name = 'function_name';
-- Check function dependencies
mcp__supabase__execute_sql
SELECT
p.proname as function_name,
pg_get_functiondef(p.oid) as definition
FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE n.nspname = 'public'
AND p.proname = 'function_name';
Step 2: Create/Replace Function
-- CREATE or REPLACE function
CREATE OR REPLACE FUNCTION public.function_name(
p_param1 TYPE,
p_param2 TYPE DEFAULT default_value
)
RETURNS return_type
LANGUAGE plpgsql
SECURITY INVOKER -- or DEFINER if needed
SET search_path = ''
AS $$
DECLARE
v_result return_type;
BEGIN
-- Function logic using fully qualified names
SELECT column_name
INTO v_result
FROM public.table_name
WHERE condition = p_param1;
RETURN v_result;
EXCEPTION
WHEN OTHERS THEN
RAISE EXCEPTION 'Error in function_name: %', SQLERRM;
END;
$$;
-- Grant permissions
GRANT EXECUTE ON FUNCTION public.function_name TO authenticated;
GRANT EXECUTE ON FUNCTION public.function_name TO anon;
-- Add comment
COMMENT ON FUNCTION public.function_name IS 'Description of what this function does';
Step 3: Drop Function (if needed)
-- Check if function exists first
mcp__supabase__execute_sql
SELECT proname FROM pg_proc
WHERE proname = 'function_name';
-- Drop with signature
DROP FUNCTION IF EXISTS public.function_name(TYPE, TYPE);
Step 3: Ask for Confirmation (MANDATORY)
⚠️ CRITICAL: NEVER skip this step.
Present the function change plan:
## Migration Plan
**Schema Object:** `public.function_name`
**Type:** Function
**Action:** [CREATE/REPLACE/DROP]
### Function Signature:
| Parameter | Type | Default |
|-----------|------|---------|
| p_param1 | TYPE | - |
| p_param2 | TYPE | default_value |
**Returns:** return_type
### SQL to Execute:
```sql
[Function SQL statement]
Files to Update:
| File | Change Required |
|---|---|
supabase/migrations/YYYYMMDD_*.sql |
Document function |
supabase/setup/02_functions.sql |
Add/Update function definition |
| Application files calling this function | Update calls if signature changed |
Dependencies to Check:
- RLS policies using this function
- Triggers using this function
- Views using this function
- Application code calling this function
Proceed with function change? (yes/no)
### Step 4: Update References
- Check RLS policies using the function
- Check triggers using the function
- Check views using the function
- Check application code calling the function
---
## View Change Workflow
### Step 1: Query Existing View
```sql
mcp__supabase__execute_sql
SELECT
table_name as view_name,
view_definition
FROM information_schema.views
WHERE table_schema = 'public'
AND table_name = 'view_name';
-- Check view dependencies
mcp__supabase__execute_sql
SELECT
dependent_ns.nspname as dependent_schema,
dependent_view.relname as dependent_view
FROM pg_depend
JOIN pg_rewrite ON pg_depend.objid = pg_rewrite.oid
JOIN pg_class as dependent_view ON pg_rewrite.ev_class = dependent_view.oid
JOIN pg_namespace dependent_ns ON dependent_view.relnamespace = dependent_ns.oid
WHERE pg_depend.refobjid = 'public.view_name'::regclass;
Step 2: Create/Replace View
-- CREATE or REPLACE view
CREATE OR REPLACE VIEW public.view_name AS
SELECT
t1.id,
t1.name,
t2.related_field
FROM public.table1 t1
LEFT JOIN public.table2 t2 ON t1.id = t2.table1_id
WHERE t1.is_active = true;
-- Add comment
COMMENT ON VIEW public.view_name IS 'Description of view purpose';
-- Grant access (views inherit table permissions, but can be restricted)
GRANT SELECT ON public.view_name TO authenticated;
Step 3: Ask for Confirmation (MANDATORY)
⚠️ CRITICAL: NEVER skip this step.
Present the view change plan:
## Migration Plan
**Schema Object:** `public.view_name`
**Type:** View [Standard/Materialized]
**Action:** [CREATE/REPLACE/DROP]
### View Definition:
```sql
SELECT columns FROM tables WHERE conditions...
SQL to Execute:
[View SQL statement]
Files to Update:
| File | Change Required |
|---|---|
supabase/migrations/YYYYMMDD_*.sql |
Document view |
supabase/setup/05_views.sql |
Add/Update view definition |
types/[entity].ts |
Add TypeScript type for view (if needed) |
| API routes querying this view | Update if columns changed |
Dependencies:
- Tables used: [list of tables]
- Dependent views: [list if any]
Proceed with view change? (yes/no)
### Step 4: Materialized Views (if needed)
```sql
-- Create materialized view
CREATE MATERIALIZED VIEW public.mv_name AS
SELECT ...
WITH DATA;
-- Create index on materialized view
CREATE UNIQUE INDEX idx_mv_name_id ON public.mv_name(id);
-- Refresh materialized view
REFRESH MATERIALIZED VIEW CONCURRENTLY public.mv_name;
Trigger Change Workflow
Step 1: Query Existing Triggers
mcp__supabase__execute_sql
SELECT
trigger_name,
event_manipulation,
event_object_table,
action_timing,
action_statement
FROM information_schema.triggers
WHERE trigger_schema = 'public'
AND event_object_table = 'table_name';
Step 2: Create Trigger Function
-- Create trigger function first
CREATE OR REPLACE FUNCTION public.trigger_function_name()
RETURNS TRIGGER
LANGUAGE plpgsql
SECURITY INVOKER
SET search_path = ''
AS $$
BEGIN
-- For INSERT/UPDATE triggers
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
-- For DELETE triggers
-- RETURN OLD;
END;
$$;
Step 3: Create Trigger
-- Create trigger
CREATE TRIGGER trg_table_action
BEFORE INSERT OR UPDATE ON public.table_name
FOR EACH ROW
EXECUTE FUNCTION public.trigger_function_name();
-- Common trigger patterns:
-- updated_at auto-update
CREATE TRIGGER trg_table_updated_at
BEFORE UPDATE ON public.table_name
FOR EACH ROW
EXECUTE FUNCTION public.update_updated_at();
-- Audit logging
CREATE TRIGGER trg_table_audit
AFTER INSERT OR UPDATE OR DELETE ON public.table_name
FOR EACH ROW
EXECUTE FUNCTION public.audit_log();
Step 4: Ask for Confirmation (MANDATORY)
⚠️ CRITICAL: NEVER skip this step.
Present the trigger change plan:
## Migration Plan
**Schema Object:** `trg_table_action`
**Type:** Trigger
**Action:** [CREATE/DROP/ENABLE/DISABLE]
### Trigger Details:
| Property | Value |
|----------|-------|
| Table | `public.table_name` |
| Timing | BEFORE/AFTER |
| Events | INSERT/UPDATE/DELETE |
| For Each | ROW/STATEMENT |
| Function | `public.trigger_function_name()` |
### SQL to Execute:
```sql
[Trigger SQL statement]
Files to Update:
| File | Change Required |
|---|---|
supabase/migrations/YYYYMMDD_*.sql |
Document trigger |
supabase/setup/04_triggers.sql |
Add/Update trigger definition |
supabase/setup/02_functions.sql |
Add trigger function (if new) |
Proceed with trigger change? (yes/no)
### Step 5: Drop Trigger (if needed)
```sql
DROP TRIGGER IF EXISTS trg_name ON public.table_name;
DROP FUNCTION IF EXISTS public.trigger_function_name();
Index Change Workflow
Step 1: Query Existing Indexes
mcp__supabase__execute_sql
SELECT
indexname,
indexdef
FROM pg_indexes
WHERE schemaname = 'public'
AND tablename = 'table_name';
-- Check index usage
mcp__supabase__execute_sql
SELECT
schemaname,
relname,
indexrelname,
idx_scan,
idx_tup_read,
idx_tup_fetch
FROM pg_stat_user_indexes
WHERE schemaname = 'public'
AND relname = 'table_name';
Step 2: Create Index
-- Standard B-tree index
CREATE INDEX IF NOT EXISTS idx_table_column
ON public.table_name(column_name);
-- Unique index
CREATE UNIQUE INDEX IF NOT EXISTS idx_table_column_unique
ON public.table_name(column_name);
-- Composite index
CREATE INDEX IF NOT EXISTS idx_table_col1_col2
ON public.table_name(column1, column2);
-- Partial index
CREATE INDEX IF NOT EXISTS idx_table_active
ON public.table_name(column_name)
WHERE is_active = true;
-- GIN index for JSONB
CREATE INDEX IF NOT EXISTS idx_table_jsonb
ON public.table_name USING GIN (jsonb_column);
-- GiST index for full-text search
CREATE INDEX IF NOT EXISTS idx_table_search
ON public.table_name USING GIN (to_tsvector('english', text_column));
Step 3: Ask for Confirmation (MANDATORY)
⚠️ CRITICAL: NEVER skip this step.
Present the index change plan:
## Migration Plan
**Schema Object:** `idx_table_column`
**Type:** Index
**Action:** [CREATE/DROP]
### Index Details:
| Property | Value |
|----------|-------|
| Table | `public.table_name` |
| Columns | `column1, column2` |
| Type | B-tree/GIN/GiST/HASH |
| Unique | Yes/No |
| Partial | `WHERE condition` (if applicable) |
### SQL to Execute:
```sql
[Index SQL statement]
Performance Impact:
- Write Performance: [Slight overhead on INSERT/UPDATE/DELETE]
- Read Performance: [Improved for queries on indexed columns]
- Storage: [Additional disk space required]
Files to Update:
| File | Change Required |
|---|---|
supabase/migrations/YYYYMMDD_*.sql |
Document index |
Proceed with index change? (yes/no)
### Step 4: Drop Index (if needed)
```sql
DROP INDEX IF EXISTS public.idx_name;
RLS Policy Workflow
Step 1: Query Existing Policies
mcp__supabase__execute_sql
SELECT
policyname,
permissive,
roles,
cmd,
qual as using_expression,
with_check as with_check_expression
FROM pg_policies
WHERE schemaname = 'public'
AND tablename = 'table_name';
-- Check if RLS is enabled
mcp__supabase__execute_sql
SELECT
tablename,
rowsecurity
FROM pg_tables
WHERE schemaname = 'public'
AND tablename = 'table_name';
Step 2: Enable RLS
-- Enable RLS on table
ALTER TABLE public.table_name ENABLE ROW LEVEL SECURITY;
-- Force RLS for table owner too
ALTER TABLE public.table_name FORCE ROW LEVEL SECURITY;
Step 3: Create Policies
-- SELECT policy (USING only)
CREATE POLICY "Users can view own records"
ON public.table_name
FOR SELECT
TO authenticated
USING ((SELECT auth.uid()) = user_id);
-- INSERT policy (WITH CHECK only)
CREATE POLICY "Users can insert own records"
ON public.table_name
FOR INSERT
TO authenticated
WITH CHECK ((SELECT auth.uid()) = user_id);
-- UPDATE policy (both USING and WITH CHECK)
CREATE POLICY "Users can update own records"
ON public.table_name
FOR UPDATE
TO authenticated
USING ((SELECT auth.uid()) = user_id)
WITH CHECK ((SELECT auth.uid()) = user_id);
-- DELETE policy (USING only)
CREATE POLICY "Users can delete own records"
ON public.table_name
FOR DELETE
TO authenticated
USING ((SELECT auth.uid()) = user_id);
Step 4: Ask for Confirmation (MANDATORY)
⚠️ CRITICAL: NEVER skip this step.
Present the RLS policy change plan:
## Migration Plan
**Schema Object:** RLS Policies for `public.table_name`
**Type:** Row Level Security
**Action:** [CREATE/ALTER/DROP/ENABLE]
### Policy Summary:
| Policy Name | Command | Roles | Action |
|-------------|---------|-------|--------|
| "Users can view own records" | SELECT | authenticated | CREATE |
| "Users can insert own records" | INSERT | authenticated | CREATE |
| "Users can update own records" | UPDATE | authenticated | CREATE |
| "Users can delete own records" | DELETE | authenticated | CREATE |
### SQL to Execute:
```sql
[RLS Policy SQL statements]
Required Indexes:
| Index | Columns | Purpose |
|---|---|---|
idx_table_user_id |
user_id |
Policy performance |
Security Considerations:
- USING clause: [Expression for SELECT/UPDATE/DELETE]
- WITH CHECK clause: [Expression for INSERT/UPDATE]
- Permissive/Restrictive: [Type]
Files to Update:
| File | Change Required |
|---|---|
supabase/migrations/YYYYMMDD_*.sql |
Document policies |
supabase/setup/03_policies.sql |
Add/Update policy definitions |
Proceed with RLS policy change? (yes/no)
### Step 5: Create Required Indexes
```sql
-- ALWAYS create indexes on columns used in policies
CREATE INDEX IF NOT EXISTS idx_table_user_id
ON public.table_name(user_id);
Common Patterns
Standard Column Types
| Purpose | PostgreSQL Type | TypeScript Type | Default |
|---|---|---|---|
| Primary key | UUID |
string |
gen_random_uuid() |
| Foreign key | UUID REFERENCES table(id) |
string |
none |
| Boolean flag | BOOLEAN |
boolean |
false |
| Short text | VARCHAR(n) |
string |
'' or null |
| Long text | TEXT |
string |
'' or null |
| Timestamp | TIMESTAMPTZ |
string |
now() or null |
| Integer | INTEGER |
number |
0 or null |
| Decimal | NUMERIC(p,s) |
number |
0 or null |
| JSON | JSONB |
Record<string, any> |
'{}'::jsonb |
| Array | TYPE[] |
TYPE[] |
'{}' |
| Enum | custom_enum |
string (union) |
first value |
File Location Patterns
| Layer | Location Pattern |
|---|---|
| Types | types/[entity].ts |
| API Routes | app/api/[module]/route.ts |
| API Single | app/api/[module]/[id]/route.ts |
| Services | services/[module]/[entity]-service.ts or lib/services/[module]/ |
| Hooks | hooks/[module]/use-[entity].ts |
| Pages | app/[route-group]/[module]/page.tsx |
| Migrations | supabase/migrations/YYYYMMDD_*.sql |
| Functions | supabase/setup/02_functions.sql |
| Views | supabase/setup/05_views.sql |
| Policies | supabase/setup/03_policies.sql |
| Triggers | supabase/setup/04_triggers.sql |
Completion Checklist
For Column Changes
- Current schema queried via MCP
- Changes validated (existence, dependencies)
- Migration query created
- User confirmation received
- Migration applied and verified
- Migration file documented
- TypeScript types updated
- API routes updated
- Service files checked
- UI components checked
For Function/View/Trigger Changes
- Existing object queried via MCP
- Dependencies checked
- Object created/modified
- Permissions granted
- Callers/references updated
- Migration file documented
For RLS Policy Changes
- Existing policies queried
- RLS enabled on table
- Policies created (SELECT, INSERT, UPDATE, DELETE separately)
- Required indexes created
- Policies tested with different roles
Integration with TodoWrite
// Column change todo list
[
{ content: "Query current schema via MCP", status: "pending", activeForm: "Querying schema" },
{ content: "Validate changes", status: "pending", activeForm: "Validating changes" },
{ content: "Create migration query", status: "pending", activeForm: "Creating query" },
{ content: "Get user confirmation", status: "pending", activeForm: "Getting confirmation" },
{ content: "Apply migration via MCP", status: "pending", activeForm: "Applying migration" },
{ content: "Update migration file", status: "pending", activeForm: "Updating migration file" },
{ content: "Update TypeScript types", status: "pending", activeForm: "Updating types" },
{ content: "Update API routes", status: "pending", activeForm: "Updating API routes" },
{ content: "Update service files", status: "pending", activeForm: "Updating services" },
{ content: "Check UI components", status: "pending", activeForm: "Checking UI" }
]
// Function/View change todo list
[
{ content: "Query existing object via MCP", status: "pending", activeForm: "Querying object" },
{ content: "Check dependencies", status: "pending", activeForm: "Checking dependencies" },
{ content: "Create/modify object", status: "pending", activeForm: "Creating object" },
{ content: "Grant permissions", status: "pending", activeForm: "Granting permissions" },
{ content: "Update callers/references", status: "pending", activeForm: "Updating references" },
{ content: "Document in migration file", status: "pending", activeForm: "Documenting" }
]
MCP Commands Quick Reference
# List all tables
mcp__supabase__list_tables
# Execute SQL query
mcp__supabase__execute_sql
Query: [SQL]
# Apply migration
mcp__supabase__apply_migration
Name: [migration_name]
Query: [SQL]
# List migrations
mcp__supabase__list_migrations
# Get logs
mcp__supabase__get_logs
Service: postgres
# Generate TypeScript types
mcp__supabase__generate_typescript_types
Skill Version: 2.1.0 Updated: 2025-12-30 Compatibility: Any Next.js + Supabase project
Changelog
- v2.1.0 - Added mandatory confirmation step with detailed Migration Plan templates for all schema change types (Column, Function, View, Trigger, Index, RLS Policy)
- v2.0.0 - Extended to support functions, views, triggers, indexes, RLS policies; made project-agnostic
- v1.0.0 - Initial column-focused workflow for JKKN COE project