Supabase RPC Functions¶
Overview¶
This document describes the PostgreSQL RPC (Remote Procedure Call) functions used to bypass PostgREST schema caching issues when using Supabase with Railway's transaction pooling.
Why RPC Functions?¶
Problem:
- Railway + Supabase use transaction pooling (port 6543)
- Direct PostgreSQL connections don't work on Railway
- PostgREST (Supabase's REST API layer) has schema caching issues
- Using .table().select() sometimes returns 404 errors on newly created tables
Solution:
- Create PostgreSQL functions to wrap database operations
- Call functions via .rpc('function_name', args) instead of .table().select()
- Functions bypass PostgREST cache and always work with transaction pooling
Available RPC Functions¶
User Operations¶
get_user_by_phone_number(phone_number text)¶
Get user by phone number.
Usage:
response = client.rpc('get_user_by_phone_number', phone).execute()
user = response.data[0] if response.data else None
create_user_record(phone_number text)¶
Create a new user.
Usage:
response = client.rpc('create_user_record', phone).execute()
user = response.data[0] if response.data else None
Conversation Operations¶
get_conversation_by_guid(p_chat_guid text)¶
Get conversation by chat GUID.
Usage:
response = client.rpc('get_conversation_by_guid', chat_guid).execute()
conversation = response.data[0] if response.data else None
create_conversation_record(p_chat_guid text, p_mode text, p_participants jsonb)¶
Create a new conversation.
Usage:
import json
participants_json = json.dumps(["+15551234567"])
response = client.rpc('create_conversation_record', chat_guid, mode, participants_json).execute()
conversation = response.data[0] if response.data else None
Message Operations¶
create_message_record(...)¶
Create a new message.
Parameters:
- p_conversation_id (uuid)
- p_sender (text)
- p_text (text)
- p_timestamp (timestamptz)
- p_message_type (text, default: 'text')
- p_message_metadata (jsonb, default: '{}')
Usage:
response = client.rpc(
'create_message_record',
conversation_id,
sender,
text,
timestamp.isoformat(),
message_type,
json.dumps(metadata)
).execute()
message = response.data[0] if response.data else None
Note: The function returns msg_timestamp instead of timestamp to avoid reserved word conflicts.
get_recent_messages_for_conversation(p_conversation_id uuid, p_limit integer, p_hours_ago integer)¶
Get recent messages for a conversation.
Usage:
response = client.rpc(
'get_recent_messages_for_conversation',
conversation_id,
20, # limit
24 # hours_ago
).execute()
messages = response.data if response.data else []
get_last_message_for_conversation(p_conversation_id uuid)¶
Get the most recent message for a conversation.
Usage:
response = client.rpc('get_last_message_for_conversation', conversation_id).execute()
message = response.data[0] if response.data else None
Implementation Details¶
Type Casting¶
All functions cast VARCHAR columns to TEXT to ensure type compatibility:
Security¶
All functions are created with SECURITY DEFINER to run with the owner's privileges, bypassing RLS policies.
Column Name Differences¶
- Database column:
timestamp→ Function returns:msg_timestamp - Database column:
message_metadata→ Function returns:meta_data
These are renamed in Python code (app/database/supabase_db.py) to maintain API compatibility.
Migration History¶
- 20251115_drop_and_recreate_rpc_functions - Initial RPC functions
- 20251115_fix_rpc_function_types - Fixed VARCHAR→TEXT type mismatches
Testing¶
Test RPC functions directly in Supabase SQL editor:
-- Test get_conversation_by_guid
SELECT * FROM get_conversation_by_guid('test_chat');
-- Test create_user_record
SELECT * FROM create_user_record('+15551234567');
References¶
- Supabase Python Client: https://supabase.com/docs/reference/python/introduction
- PostgREST Documentation: https://postgrest.org/en/stable/
- Railway + Supabase Guide: https://docs.railway.app/guides/supabase