0
# Edge Functions
1
2
Serverless function invocation and management for running custom server-side logic at the edge. Provides seamless integration with Supabase Edge Functions for executing TypeScript/JavaScript code with automatic scaling and global distribution.
3
4
## Capabilities
5
6
### Functions Client Access
7
8
Access the edge functions client through the main Supabase client instance.
9
10
```python { .api }
11
@property
12
def functions(self) -> SyncFunctionsClient | AsyncFunctionsClient:
13
"""
14
Functions client providing edge function invocation capabilities.
15
16
Returns:
17
Functions client instance (sync or async) with methods for:
18
- Function invocation with custom parameters
19
- Request/response handling with proper error management
20
- Integration with authentication context
21
- Custom headers and configuration options
22
"""
23
```
24
25
### Function Invocation
26
27
Execute edge functions with parameters, custom headers, and response handling.
28
29
```python { .api }
30
def invoke(
31
self,
32
function_name: str,
33
invoke_options: dict = None
34
) -> FunctionsResponse:
35
"""
36
Invoke an edge function by name.
37
38
Parameters:
39
- function_name: Name of the edge function to invoke
40
- invoke_options: Invocation options including body, headers, method, etc.
41
42
Returns:
43
FunctionsResponse with data, headers, status, and error information
44
45
Raises:
46
FunctionsHttpError: If HTTP request fails
47
FunctionsRelayError: If function relay fails
48
FunctionsError: For general function errors
49
50
Note: For async clients, this method is async
51
"""
52
```
53
54
**Usage Examples:**
55
56
```python
57
# Basic function invocation
58
response = supabase.functions.invoke("hello-world")
59
print(f"Response: {response.data}")
60
61
# Function with parameters
62
response = supabase.functions.invoke("greeting", {
63
"body": {"name": "Alice", "language": "en"}
64
})
65
print(f"Greeting: {response.data}")
66
67
# Function with custom headers
68
response = supabase.functions.invoke("protected-function", {
69
"headers": {
70
"Authorization": f"Bearer {user_token}",
71
"Content-Type": "application/json"
72
},
73
"body": {"action": "get_user_data"}
74
})
75
76
# POST request with JSON data
77
response = supabase.functions.invoke("process-data", {
78
"method": "POST",
79
"headers": {"Content-Type": "application/json"},
80
"body": {
81
"data": [1, 2, 3, 4, 5],
82
"operation": "sum",
83
"metadata": {"source": "python-client"}
84
}
85
})
86
87
# Handle response data
88
if response.data:
89
result = response.data
90
print(f"Processing result: {result}")
91
else:
92
print(f"Function failed: {response.error}")
93
94
# Async function invocation
95
async def call_async_function():
96
client = await create_async_client(url, key)
97
response = await client.functions.invoke("async-processor", {
98
"body": {"items": ["a", "b", "c"]}
99
})
100
return response.data
101
```
102
103
### Response Handling
104
105
Process function responses, handle errors, and extract data from function execution results.
106
107
```python { .api }
108
# FunctionsResponse attributes (from supabase_functions)
109
class FunctionsResponse:
110
"""Response from edge function invocation."""
111
112
data: Any
113
"""Function response data (parsed JSON or raw text)"""
114
115
error: Optional[str]
116
"""Error message if function execution failed"""
117
118
status: int
119
"""HTTP status code from function response"""
120
121
headers: Dict[str, str]
122
"""Response headers from function"""
123
```
124
125
**Usage Examples:**
126
127
```python
128
# Comprehensive response handling
129
response = supabase.functions.invoke("data-processor", {
130
"body": {"input": "test data"}
131
})
132
133
# Check response status
134
if response.status == 200:
135
print("Function executed successfully")
136
data = response.data
137
print(f"Result: {data}")
138
elif response.status == 400:
139
print(f"Bad request: {response.error}")
140
elif response.status == 500:
141
print(f"Function error: {response.error}")
142
else:
143
print(f"Unexpected status {response.status}: {response.error}")
144
145
# Access response headers
146
print(f"Content-Type: {response.headers.get('content-type')}")
147
print(f"Execution time: {response.headers.get('x-execution-time')}")
148
149
# Handle different response types
150
def process_function_response(response):
151
"""Process function response based on content type"""
152
content_type = response.headers.get('content-type', '')
153
154
if 'application/json' in content_type:
155
# JSON response
156
return response.data
157
elif 'text/plain' in content_type:
158
# Text response
159
return str(response.data)
160
elif 'application/octet-stream' in content_type:
161
# Binary response
162
return response.data # Raw bytes
163
else:
164
# Unknown content type
165
return response.data
166
167
# Example with file processing function
168
response = supabase.functions.invoke("image-resize", {
169
"body": {"image_url": "https://example.com/image.jpg", "width": 300},
170
"headers": {"Accept": "application/json"}
171
})
172
173
if response.status == 200:
174
result = response.data
175
print(f"Resized image URL: {result['resized_url']}")
176
print(f"Original size: {result['original_size']}")
177
print(f"New size: {result['new_size']}")
178
```
179
180
### Authentication Integration
181
182
Invoke functions with user authentication context and handle auth-dependent operations.
183
184
```python { .api }
185
# Functions automatically inherit auth context from client
186
# No explicit API methods, but functions receive user context
187
```
188
189
**Usage Examples:**
190
191
```python
192
# Functions automatically receive user auth context
193
# The user's JWT token is automatically passed to functions
194
195
# Get current user session
196
session = supabase.auth.get_session()
197
if session:
198
# Function will receive authenticated user context
199
response = supabase.functions.invoke("user-profile", {
200
"body": {"operation": "get_preferences"}
201
})
202
203
user_data = response.data
204
print(f"User preferences: {user_data}")
205
else:
206
print("User not authenticated")
207
208
# Functions can access user data through auth context
209
response = supabase.functions.invoke("protected-operation", {
210
"body": {"action": "update_profile", "data": {"theme": "dark"}}
211
})
212
213
# Function can use auth to authorize operations
214
if response.status == 401:
215
print("Authentication required")
216
elif response.status == 403:
217
print("Insufficient permissions")
218
elif response.status == 200:
219
print(f"Operation successful: {response.data}")
220
221
# Example: User-specific data processing
222
def process_user_data(operation, data):
223
"""Process user data through edge function"""
224
# Check if user is authenticated
225
session = supabase.auth.get_session()
226
if not session:
227
raise Exception("Authentication required")
228
229
response = supabase.functions.invoke("user-data-processor", {
230
"body": {
231
"operation": operation,
232
"data": data,
233
"user_id": session.user.id # Explicit user ID
234
}
235
})
236
237
if response.status != 200:
238
raise Exception(f"Function failed: {response.error}")
239
240
return response.data
241
242
# Usage
243
try:
244
result = process_user_data("calculate_stats", {"period": "monthly"})
245
print(f"Stats: {result}")
246
except Exception as e:
247
print(f"Error: {e}")
248
```
249
250
### Custom Headers and Configuration
251
252
Send custom headers, configure request options, and handle different HTTP methods.
253
254
```python { .api }
255
# Invoke options configuration
256
invoke_options = {
257
"method": str, # HTTP method (GET, POST, PUT, etc.)
258
"headers": Dict[str, str], # Custom request headers
259
"body": Any, # Request body (JSON serializable)
260
"region": str, # Function region (optional)
261
"timeout": int # Request timeout in milliseconds
262
}
263
```
264
265
**Usage Examples:**
266
267
```python
268
# Custom headers for API integration
269
response = supabase.functions.invoke("external-api-proxy", {
270
"method": "POST",
271
"headers": {
272
"Content-Type": "application/json",
273
"X-API-Key": "external-service-key",
274
"User-Agent": "MyApp/1.0"
275
},
276
"body": {
277
"endpoint": "https://api.external.com/data",
278
"params": {"limit": 100}
279
}
280
})
281
282
# Different HTTP methods
283
# GET request
284
get_response = supabase.functions.invoke("data-fetcher", {
285
"method": "GET",
286
"headers": {"Accept": "application/json"}
287
})
288
289
# POST request with form data
290
post_response = supabase.functions.invoke("form-processor", {
291
"method": "POST",
292
"headers": {"Content-Type": "application/x-www-form-urlencoded"},
293
"body": "name=John&email=john@example.com"
294
})
295
296
# PUT request for updates
297
put_response = supabase.functions.invoke("resource-updater", {
298
"method": "PUT",
299
"headers": {"Content-Type": "application/json"},
300
"body": {"id": 123, "name": "Updated Name"}
301
})
302
303
# DELETE request
304
delete_response = supabase.functions.invoke("resource-deleter", {
305
"method": "DELETE",
306
"headers": {"Accept": "application/json"},
307
"body": {"id": 123}
308
})
309
310
# Configure timeout for long-running functions
311
response = supabase.functions.invoke("data-processor", {
312
"body": {"large_dataset": data},
313
"timeout": 60000 # 60 seconds
314
})
315
316
# Regional function invocation
317
response = supabase.functions.invoke("region-specific", {
318
"region": "us-east-1",
319
"body": {"data": "region-specific-data"}
320
})
321
```
322
323
### Batch Operations and Utilities
324
325
Handle multiple function calls, batch operations, and utility patterns.
326
327
**Usage Examples:**
328
329
```python
330
# Batch function calls
331
async def batch_invoke_functions(functions_data):
332
"""Invoke multiple functions concurrently (async)"""
333
client = await create_async_client(url, key)
334
335
tasks = []
336
for func_name, options in functions_data.items():
337
task = client.functions.invoke(func_name, options)
338
tasks.append(task)
339
340
results = await asyncio.gather(*tasks, return_exceptions=True)
341
342
response_map = {}
343
for i, (func_name, _) in enumerate(functions_data.items()):
344
response_map[func_name] = results[i]
345
346
return response_map
347
348
# Batch processing example
349
batch_data = {
350
"process-images": {
351
"body": {"images": ["img1.jpg", "img2.jpg"]}
352
},
353
"send-notifications": {
354
"body": {"users": ["user1", "user2"], "message": "Hello"}
355
},
356
"update-analytics": {
357
"body": {"events": ["login", "purchase"]}
358
}
359
}
360
361
# async batch execution
362
results = await batch_invoke_functions(batch_data)
363
for func_name, response in results.items():
364
if isinstance(response, Exception):
365
print(f"{func_name} failed: {response}")
366
else:
367
print(f"{func_name} result: {response.data}")
368
369
# Synchronous batch with error handling
370
def batch_invoke_sync(function_calls):
371
"""Invoke multiple functions synchronously with error handling"""
372
results = {}
373
374
for func_name, options in function_calls.items():
375
try:
376
response = supabase.functions.invoke(func_name, options)
377
results[func_name] = {
378
"success": True,
379
"data": response.data,
380
"status": response.status
381
}
382
except Exception as e:
383
results[func_name] = {
384
"success": False,
385
"error": str(e),
386
"status": None
387
}
388
389
return results
390
391
# Pipeline processing
392
def function_pipeline(data, pipeline_steps):
393
"""Process data through a pipeline of functions"""
394
current_data = data
395
396
for step in pipeline_steps:
397
func_name = step["function"]
398
params = step.get("params", {})
399
400
response = supabase.functions.invoke(func_name, {
401
"body": {**params, "input": current_data}
402
})
403
404
if response.status != 200:
405
raise Exception(f"Pipeline failed at {func_name}: {response.error}")
406
407
current_data = response.data
408
409
return current_data
410
411
# Usage
412
pipeline = [
413
{"function": "validate-data", "params": {"schema": "user_data"}},
414
{"function": "transform-data", "params": {"format": "normalized"}},
415
{"function": "enrich-data", "params": {"source": "external_api"}},
416
{"function": "store-data", "params": {"table": "processed_users"}}
417
]
418
419
try:
420
result = function_pipeline(user_input_data, pipeline)
421
print(f"Pipeline result: {result}")
422
except Exception as e:
423
print(f"Pipeline failed: {e}")
424
```
425
426
### Error Handling and Debugging
427
428
Handle function errors, debug issues, and implement retry logic.
429
430
```python { .api }
431
# Functions exceptions (from supabase_functions)
432
class FunctionsError(Exception):
433
"""Base class for functions errors"""
434
435
class FunctionsHttpError(Exception):
436
"""HTTP request errors when invoking functions"""
437
438
class FunctionsRelayError(Exception):
439
"""Function relay and communication errors"""
440
```
441
442
**Error Handling Examples:**
443
444
```python
445
from supabase_functions.errors import FunctionsError, FunctionsHttpError, FunctionsRelayError
446
447
# Comprehensive error handling
448
def invoke_with_retry(func_name, options, max_retries=3):
449
"""Invoke function with retry logic"""
450
for attempt in range(max_retries):
451
try:
452
response = supabase.functions.invoke(func_name, options)
453
454
# Check for successful response
455
if response.status == 200:
456
return response
457
elif response.status >= 500:
458
# Server error - retry
459
if attempt < max_retries - 1:
460
print(f"Server error (attempt {attempt + 1}), retrying...")
461
time.sleep(2 ** attempt) # Exponential backoff
462
continue
463
else:
464
raise Exception(f"Server error after {max_retries} attempts")
465
else:
466
# Client error - don't retry
467
raise Exception(f"Client error {response.status}: {response.error}")
468
469
except FunctionsHttpError as e:
470
# Network/HTTP error - retry
471
if attempt < max_retries - 1:
472
print(f"HTTP error (attempt {attempt + 1}): {e}")
473
time.sleep(2 ** attempt)
474
continue
475
else:
476
raise Exception(f"HTTP error after {max_retries} attempts: {e}")
477
478
except FunctionsRelayError as e:
479
# Relay error - retry
480
if attempt < max_retries - 1:
481
print(f"Relay error (attempt {attempt + 1}): {e}")
482
time.sleep(2 ** attempt)
483
continue
484
else:
485
raise Exception(f"Relay error after {max_retries} attempts: {e}")
486
487
except FunctionsError as e:
488
# General function error - don't retry
489
raise Exception(f"Function error: {e}")
490
491
# Error categorization and handling
492
def handle_function_error(response):
493
"""Categorize and handle function errors"""
494
status = response.status
495
error = response.error
496
497
if status == 400:
498
return {"type": "client_error", "message": "Invalid request", "retry": False}
499
elif status == 401:
500
return {"type": "auth_error", "message": "Authentication required", "retry": False}
501
elif status == 403:
502
return {"type": "permission_error", "message": "Insufficient permissions", "retry": False}
503
elif status == 404:
504
return {"type": "not_found", "message": "Function not found", "retry": False}
505
elif status == 429:
506
return {"type": "rate_limit", "message": "Rate limit exceeded", "retry": True}
507
elif status >= 500:
508
return {"type": "server_error", "message": "Server error", "retry": True}
509
else:
510
return {"type": "unknown", "message": error, "retry": False}
511
512
# Usage with error handling
513
try:
514
response = invoke_with_retry("unreliable-function", {
515
"body": {"data": "test"}
516
})
517
print(f"Success: {response.data}")
518
519
except Exception as e:
520
print(f"Function invocation failed: {e}")
521
522
# Debugging and logging
523
def invoke_with_logging(func_name, options):
524
"""Invoke function with detailed logging"""
525
print(f"Invoking function: {func_name}")
526
print(f"Options: {options}")
527
528
start_time = time.time()
529
530
try:
531
response = supabase.functions.invoke(func_name, options)
532
533
duration = time.time() - start_time
534
print(f"Function completed in {duration:.2f}s")
535
print(f"Status: {response.status}")
536
print(f"Headers: {response.headers}")
537
538
if response.status == 200:
539
print(f"Response data: {response.data}")
540
else:
541
print(f"Error: {response.error}")
542
543
return response
544
545
except Exception as e:
546
duration = time.time() - start_time
547
print(f"Function failed after {duration:.2f}s: {e}")
548
raise
549
550
# Function health check
551
def check_function_health(func_name):
552
"""Check if function is healthy and responsive"""
553
try:
554
response = supabase.functions.invoke(func_name, {
555
"method": "GET",
556
"timeout": 5000 # 5 second timeout
557
})
558
559
return {
560
"healthy": response.status == 200,
561
"status": response.status,
562
"response_time": response.headers.get("x-execution-time"),
563
"error": response.error if response.status != 200 else None
564
}
565
except Exception as e:
566
return {
567
"healthy": False,
568
"status": None,
569
"response_time": None,
570
"error": str(e)
571
}
572
573
# Monitor function performance
574
health = check_function_health("critical-function")
575
if health["healthy"]:
576
print(f"Function is healthy (response time: {health['response_time']})")
577
else:
578
print(f"Function is unhealthy: {health['error']}")
579
```
580
581
### Performance and Best Practices
582
583
```python
584
# Optimize function calls for performance and reliability
585
586
# 1. Use appropriate timeouts
587
short_timeout_response = supabase.functions.invoke("quick-function", {
588
"body": {"data": "test"},
589
"timeout": 5000 # 5 seconds for quick operations
590
})
591
592
long_timeout_response = supabase.functions.invoke("heavy-processing", {
593
"body": {"large_dataset": data},
594
"timeout": 120000 # 2 minutes for heavy processing
595
})
596
597
# 2. Minimize payload size
598
# Good: Send only necessary data
599
response = supabase.functions.invoke("processor", {
600
"body": {"ids": [1, 2, 3], "operation": "summarize"}
601
})
602
603
# Avoid: Sending large unnecessary data
604
# response = supabase.functions.invoke("processor", {
605
# "body": {"full_records": large_dataset, "operation": "summarize"}
606
# })
607
608
# 3. Use appropriate error handling
609
def robust_function_call(func_name, data):
610
"""Make robust function calls with proper error handling"""
611
try:
612
response = supabase.functions.invoke(func_name, {
613
"body": data,
614
"timeout": 30000
615
})
616
617
if response.status == 200:
618
return {"success": True, "data": response.data}
619
else:
620
return {"success": False, "error": response.error, "status": response.status}
621
622
except Exception as e:
623
return {"success": False, "error": str(e), "status": None}
624
625
# 4. Cache function results when appropriate
626
function_cache = {}
627
628
def cached_function_call(func_name, cache_key, data):
629
"""Cache function results to avoid redundant calls"""
630
if cache_key in function_cache:
631
return function_cache[cache_key]
632
633
result = robust_function_call(func_name, data)
634
if result["success"]:
635
function_cache[cache_key] = result
636
637
return result
638
639
# 5. Async batch processing for multiple independent calls
640
async def process_items_concurrently(items):
641
"""Process multiple items concurrently using functions"""
642
client = await create_async_client(url, key)
643
644
tasks = []
645
for item in items:
646
task = client.functions.invoke("item-processor", {
647
"body": {"item": item}
648
})
649
tasks.append(task)
650
651
responses = await asyncio.gather(*tasks, return_exceptions=True)
652
653
results = []
654
for i, response in enumerate(responses):
655
if isinstance(response, Exception):
656
results.append({"item": items[i], "success": False, "error": str(response)})
657
else:
658
results.append({"item": items[i], "success": True, "data": response.data})
659
660
return results
661
```