Database Migrations
This directory contains SQL migration scripts for the Setu (Nepal Justice Weaver) platform.
Directory Structure
database/
βββ migrations/
βββ README.md # This file
βββ 001_create_chat_tables.sql # Chat persistence schema
βββ [future migrations...]
How to Apply Migrations
Option 1: Via Supabase Dashboard (Recommended)
Login to your Supabase Dashboard
- Go to https://app.supabase.com
- Select your project
Open SQL Editor
- Click on "SQL Editor" in the left sidebar
- Click "New Query"
Copy and Paste SQL
- Open the migration file (e.g.,
001_create_chat_tables.sql) - Copy the entire contents
- Paste into the SQL Editor
- Open the migration file (e.g.,
Run the Migration
- Click "Run" or press
Ctrl+Enter - Wait for success message
- Click "Run" or press
Verify
- Go to "Table Editor" in the left sidebar
- You should see:
chat_conversationschat_messages
Option 2: Via Supabase CLI
# Install Supabase CLI (if not already installed)
npm install -g supabase
# Login to Supabase
supabase login
# Link to your project
supabase link --project-ref your-project-id
# Run migration
supabase db push --file database/migrations/001_create_chat_tables.sql
Option 3: Via Python Script
from supabase import create_client
import os
supabase_url = os.getenv("SUPABASE_URL")
supabase_key = os.getenv("SUPABASE_SERVICE_ROLE_KEY")
supabase = create_client(supabase_url, supabase_key)
# Read SQL file
with open("database/migrations/001_create_chat_tables.sql", "r") as f:
sql = f.read()
# Execute (Note: Supabase Python client doesn't support raw SQL directly,
# so use the dashboard method instead)
Migration Files
001_create_chat_tables.sql
Purpose: Creates the database schema for chat persistence
What it creates:
chat_conversationstable - Stores conversation metadatachat_messagestable - Stores individual messages- Indexes for performance optimization
- Row Level Security (RLS) policies
- Automatic timestamp update trigger
Tables Created:
chat_conversations
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key (auto-generated) |
| user_id | UUID | Foreign key to auth.users(id) |
| title | TEXT | Conversation title |
| created_at | TIMESTAMPTZ | Creation timestamp |
| updated_at | TIMESTAMPTZ | Last update timestamp (auto-updated) |
chat_messages
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key (auto-generated) |
| conversation_id | UUID | Foreign key to chat_conversations(id) |
| role | TEXT | 'user' or 'assistant' |
| content | TEXT | Message content |
| timestamp | TIMESTAMPTZ | Message timestamp |
| metadata | JSONB | Optional metadata (sources, tokens, etc.) |
Security Features:
- β Row Level Security (RLS) enabled
- β Users can only access their own conversations
- β Cascade delete (deleting user β deletes conversations β deletes messages)
- β Check constraints on role field
Verification
After running the migration, verify it worked:
Check Tables Exist
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name IN ('chat_conversations', 'chat_messages');
Expected output: ``` table_name
chat_conversations chat_messages
### Check Indexes
```sql
SELECT indexname
FROM pg_indexes
WHERE tablename IN ('chat_conversations', 'chat_messages');
Check RLS is Enabled
SELECT tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public'
AND tablename IN ('chat_conversations', 'chat_messages');
Expected output:
tablename | rowsecurity
---------------------+-------------
chat_conversations | t
chat_messages | t
Test Insert (Optional)
-- Insert a test conversation (replace with your user ID from auth.users)
INSERT INTO public.chat_conversations (user_id, title)
VALUES ('your-user-id-here', 'Test Conversation')
RETURNING *;
-- Get the conversation ID from the result above, then:
INSERT INTO public.chat_messages (conversation_id, role, content)
VALUES
('conversation-id-here', 'user', 'Test user message'),
('conversation-id-here', 'assistant', 'Test assistant response')
RETURNING *;
Rollback (If Needed)
To remove the chat tables:
-- Drop tables (cascade will remove dependent objects)
DROP TABLE IF EXISTS public.chat_messages CASCADE;
DROP TABLE IF EXISTS public.chat_conversations CASCADE;
-- Drop function
DROP FUNCTION IF EXISTS public.update_conversation_updated_at() CASCADE;
Integration with Production
When ready to integrate with the production database:
Run the migration on production Supabase
# Use the same SQL file on production instance # Just run 001_create_chat_tables.sql in production SQL EditorUpdate .env with production credentials
SUPABASE_URL=https://production-project-id.supabase.co SUPABASE_ANON_KEY=production_anon_key SUPABASE_SERVICE_ROLE_KEY=production_service_role_keyNo code changes needed
- The API routes automatically use environment variables
- Same table structure works identically
Troubleshooting
Error: "permission denied for schema public"
Solution: Make sure you're using the service_role key, not the anon key
Error: "relation already exists"
Solution: Tables already exist. Either drop them first or skip re-running migration
Error: "function uuid_generate_v4() does not exist"
Solution: Run CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; first
RLS blocking queries in SQL Editor
Solution: In SQL Editor, queries run as the service role and bypass RLS. If testing RLS, use the API routes instead.
Future Migrations
When adding new migrations:
- Create a new file:
002_migration_name.sql - Follow the same structure
- Document changes in this README
- Always include rollback instructions
Support
For questions or issues:
- Check the main project README
- Review Supabase documentation: https://supabase.com/docs
- Contact the development team