Skip to main content

Supabase RPC Functions Documentation

This document provides a comprehensive reference for all Supabase RPC (Remote Procedure Call) functions used in the project.

Overview

RPC functions are used to:

  • Access cross-schema tables (especially auth.users)
  • Perform complex database operations
  • Encapsulate business logic in the database
  • Provide secure, controlled access to sensitive data

Why RPC Functions?

Supabase Client Limitations

The Supabase Python client has limitations when accessing tables outside the public schema:

# ❌ This fails - tries to query "public.auth.users"
supabase.table("auth.users").select("*").execute()

# ✅ This works - uses RPC function
supabase.rpc('check_user_exists', {'user_id': user_id}).execute()

Benefits of RPC Approach

  • Schema-Agnostic: Can access any schema (auth, public, etc.)
  • Security: SECURITY DEFINER provides controlled access
  • Performance: Pre-compiled functions are faster
  • Type Safety: Predictable return types
  • Maintainability: Logic centralized in database

Available RPC Functions

1. User Management Functions

check_user_exists(user_id uuid) → boolean

Purpose: Check if a user exists in the auth.users table

Migration: 20250813000008_add_check_user_exists_function.sql

Definition:

CREATE OR REPLACE FUNCTION check_user_exists(user_id uuid)
RETURNS boolean
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
BEGIN
RETURN EXISTS (
SELECT 1
FROM auth.users
WHERE id = user_id
);
END;
$$;

Usage:

# Python/API Gateway
result = supabase.rpc('check_user_exists', {'user_id': 'uuid-string'}).execute()
if result.data is True:
print("User exists")

Use Cases:

  • GitHub installation callback validation
  • User existence checks before operations
  • Authentication validation

Permissions:

  • authenticated role
  • service_role

2. Node Knowledge Functions (RAG System)

match_node_knowledge(query_embedding, match_threshold, match_count, node_type_filter) → table

Purpose: Vector similarity search for node knowledge (RAG system)

Migration: 20250715000002_node_knowledge_vectors.sql

Definition:

CREATE OR REPLACE FUNCTION match_node_knowledge(
query_embedding vector(1536),
match_threshold float DEFAULT 0.3,
match_count int DEFAULT 5,
node_type_filter text DEFAULT NULL
)
RETURNS TABLE (
id uuid,
node_type varchar,
node_subtype varchar,
title varchar,
description text,
content text,
similarity float,
metadata jsonb
)

Usage:

# Workflow Agent RAG
result = supabase.rpc('match_node_knowledge', {
'query_embedding': embedding_vector,
'match_threshold': 0.3,
'match_count': 5,
'node_type_filter': 'TRIGGER_NODE'
}).execute()

Use Cases:

  • AI workflow agent knowledge retrieval
  • Node type recommendations
  • Semantic search for workflow examples

Usage Patterns

1. Authentication Validation

async def validate_user_exists(user_id: str) -> bool:
"""Validate user exists in auth.users table"""
try:
supabase_admin = get_supabase_admin()
result = supabase_admin.rpc('check_user_exists', {'user_id': user_id}).execute()
return result.data is True
except Exception as e:
logger.error(f"Error validating user {user_id}: {e}")
return False

2. RAG Knowledge Retrieval

async def get_node_knowledge(query: str, node_type: str = None) -> List[Dict]:
"""Get relevant node knowledge using vector similarity"""
# Generate embedding for query
embedding = await generate_embedding(query)

# Search for similar knowledge
result = supabase.rpc('match_node_knowledge', {
'query_embedding': embedding,
'match_threshold': 0.3,
'match_count': 5,
'node_type_filter': node_type
}).execute()

return result.data

3. Error Handling

def safe_rpc_call(function_name: str, params: dict):
"""Safe wrapper for RPC calls with error handling"""
try:
result = supabase.rpc(function_name, params).execute()
if result.data is None:
logger.warning(f"RPC {function_name} returned None")
return None
return result.data
except Exception as e:
logger.error(f"RPC {function_name} failed: {e}")
return None

Development Guidelines

Creating New RPC Functions

  1. Create Migration File:
-- Migration: 20250813000009_add_new_rpc_function.sql
CREATE OR REPLACE FUNCTION your_function_name(param1 type1, param2 type2)
RETURNS return_type
LANGUAGE plpgsql
SECURITY DEFINER -- Allows access to restricted schemas
AS $$
BEGIN
-- Function logic here
RETURN result;
END;
$$;

-- Grant permissions
GRANT EXECUTE ON FUNCTION your_function_name(type1, type2) TO authenticated;
GRANT EXECUTE ON FUNCTION your_function_name(type1, type2) TO service_role;
  1. Add to This Documentation
  2. Update Application Code
  3. Add Tests

Best Practices

✅ Do:

  • Use SECURITY DEFINER for cross-schema access
  • Grant minimal necessary permissions
  • Include comprehensive error handling
  • Document parameters and return types
  • Use type-safe parameters (uuid, not text for IDs)

❌ Don't:

  • Return sensitive data unnecessarily
  • Use dynamic SQL without sanitization
  • Grant PUBLIC execute permissions
  • Forget to handle NULL/empty cases

Testing RPC Functions

Database Level Testing:

-- Test the function directly
SELECT check_user_exists('b6c5d44a-a94f-4459-928e-90283a88f105'::uuid);

-- Test edge cases
SELECT check_user_exists('00000000-0000-0000-0000-000000000000'::uuid);

Application Level Testing:

async def test_rpc_function():
"""Test RPC function through Supabase client"""
result = supabase.rpc('check_user_exists', {
'user_id': 'b6c5d44a-a94f-4459-928e-90283a88f105'
}).execute()
assert result.data is True

Migration Files

All RPC functions are defined in migration files:

  • 20250715000002_node_knowledge_vectors.sql - RAG functions
  • 20250813000008_add_check_user_exists_function.sql - User validation

Security Considerations

SECURITY DEFINER

  • Functions run with creator's privileges
  • Allows access to auth schema
  • Use carefully - only for necessary operations

Permission Model

-- Standard permissions for most RPC functions
GRANT EXECUTE ON FUNCTION function_name TO authenticated;
GRANT EXECUTE ON FUNCTION function_name TO service_role;

-- Avoid public access
-- GRANT EXECUTE ON FUNCTION function_name TO PUBLIC; -- ❌ Don't do this

Data Exposure

  • Only return necessary data
  • Avoid exposing sensitive auth information
  • Use specific return types, not SELECT *

Troubleshooting

Common Issues

1. Permission Denied

ERROR: permission denied for function check_user_exists

Solution: Grant execute permissions to appropriate roles

2. Function Does Not Exist

ERROR: function check_user_exists(text) does not exist

Solution: Check parameter types - use uuid not text for user IDs

3. Cross-Schema Access Denied

ERROR: permission denied for schema auth

Solution: Use SECURITY DEFINER in function definition

Debugging RPC Calls

Check Function Exists:

SELECT proname, proargnames, proargtypes
FROM pg_proc
WHERE proname = 'your_function_name';

Test Function Directly:

SELECT your_function_name('test_param');

Check Permissions:

SELECT has_function_privilege('authenticated', 'your_function_name(uuid)', 'execute');

Future RPC Functions

Consider adding these RPC functions as the project grows:

User Management

  • get_user_metadata(user_id uuid) → jsonb
  • update_user_app_metadata(user_id uuid, metadata jsonb) → void
  • get_user_roles(user_id uuid) → text[]

Workflow Operations

  • get_user_workflows(user_id uuid) → table
  • validate_workflow_ownership(workflow_id uuid, user_id uuid) → boolean
  • get_workflow_execution_stats(workflow_id uuid) → jsonb

Integration Management

  • get_user_integrations(user_id uuid) → table
  • validate_integration_access(user_id uuid, provider text) → boolean
  • cleanup_expired_tokens() → integer

Resources


Last Updated: 2025-08-13 Maintainer: Backend Team Related Docs: DEPLOYMENT.md, README.md