or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mddatabase.mdfunctions.mdindex.mdrealtime.mdstorage.md

functions.mddocs/

0

# Edge Functions

1

2

Serverless function invocation system for running custom business logic at the edge. Execute TypeScript/JavaScript functions with global distribution, automatic scaling, and built-in authentication integration.

3

4

## Capabilities

5

6

### Function Invocation

7

8

Execute edge functions with custom payloads, headers, and configuration options.

9

10

```typescript { .api }

11

/**

12

* Invoke a Supabase Edge Function

13

* @param functionName - The name of the function to invoke

14

* @param options - Function invocation options

15

* @returns Promise resolving to function response or error

16

*/

17

invoke<T = any>(

18

functionName: string,

19

options?: FunctionInvokeOptions

20

): Promise<FunctionResponse<T>>;

21

22

interface FunctionInvokeOptions {

23

/** Custom headers to send with the request */

24

headers?: Record<string, string>;

25

/** Function payload data */

26

body?: any;

27

/** Preferred region for function execution */

28

region?: FunctionRegion;

29

/** HTTP method to use (defaults to POST) */

30

method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';

31

}

32

33

interface FunctionResponse<T> {

34

/** Function return data */

35

data: T | null;

36

/** Function execution error */

37

error: FunctionsError | null;

38

}

39

40

enum FunctionRegion {

41

/** Any available region (automatic selection) */

42

Any = 'any',

43

/** Asia Pacific (Tokyo) */

44

ApNortheast1 = 'ap-northeast-1',

45

/** Asia Pacific (Singapore) */

46

ApSoutheast1 = 'ap-southeast-1',

47

/** Asia Pacific (Mumbai) */

48

ApSouth1 = 'ap-south-1',

49

/** Canada (Central) */

50

CaCentral1 = 'ca-central-1',

51

/** Europe (Frankfurt) */

52

EuCentral1 = 'eu-central-1',

53

/** Europe (Ireland) */

54

EuWest1 = 'eu-west-1',

55

/** Europe (London) */

56

EuWest2 = 'eu-west-2',

57

/** South America (São Paulo) */

58

SaEast1 = 'sa-east-1',

59

/** US East (N. Virginia) */

60

UsEast1 = 'us-east-1',

61

/** US West (N. California) */

62

UsWest1 = 'us-west-1',

63

/** US West (Oregon) */

64

UsWest2 = 'us-west-2'

65

}

66

```

67

68

**Usage Examples:**

69

70

```typescript

71

// Basic function invocation

72

const { data, error } = await supabase.functions.invoke('hello-world');

73

74

if (error) {

75

console.error('Function error:', error);

76

} else {

77

console.log('Function response:', data);

78

}

79

80

// Function with payload

81

const { data, error } = await supabase.functions.invoke('process-data', {

82

body: {

83

userId: '123',

84

action: 'update_profile',

85

data: {

86

name: 'John Doe',

87

email: 'john@example.com'

88

}

89

}

90

});

91

92

// Function with custom headers

93

const { data, error } = await supabase.functions.invoke('authenticated-function', {

94

headers: {

95

'Authorization': `Bearer ${userToken}`,

96

'Content-Type': 'application/json',

97

'X-Custom-Header': 'custom-value'

98

},

99

body: { message: 'Hello from client' }

100

});

101

102

// Function with region preference

103

const { data, error } = await supabase.functions.invoke('region-specific', {

104

body: { data: 'process-me' },

105

region: FunctionRegion.EuWest1

106

});

107

108

// GET request to function

109

const { data, error } = await supabase.functions.invoke('api-endpoint', {

110

method: 'GET'

111

});

112

113

// TypeScript with typed response

114

interface ApiResponse {

115

success: boolean;

116

message: string;

117

result: any;

118

}

119

120

const { data, error } = await supabase.functions.invoke<ApiResponse>('typed-function', {

121

body: { input: 'test' }

122

});

123

124

if (data) {

125

console.log('Success:', data.success);

126

console.log('Message:', data.message);

127

console.log('Result:', data.result);

128

}

129

```

130

131

### Error Handling

132

133

Different types of errors that can occur during function execution.

134

135

```typescript { .api }

136

/**

137

* Base class for all function-related errors

138

*/

139

abstract class FunctionsError extends Error {

140

abstract name: string;

141

context?: Record<string, any>;

142

}

143

144

/**

145

* HTTP-related errors (4xx, 5xx status codes)

146

*/

147

class FunctionsHttpError extends FunctionsError {

148

name = 'FunctionsHttpError';

149

status: number;

150

statusText: string;

151

152

constructor(message: string, status: number, statusText: string) {

153

super(message);

154

this.status = status;

155

this.statusText = statusText;

156

}

157

}

158

159

/**

160

* Network/fetch-related errors

161

*/

162

class FunctionsFetchError extends FunctionsError {

163

name = 'FunctionsFetchError';

164

165

constructor(message: string) {

166

super(message);

167

}

168

}

169

170

/**

171

* Relay/connection errors

172

*/

173

class FunctionsRelayError extends FunctionsError {

174

name = 'FunctionsRelayError';

175

176

constructor(message: string) {

177

super(message);

178

}

179

}

180

```

181

182

**Usage Examples:**

183

184

```typescript

185

const { data, error } = await supabase.functions.invoke('my-function', {

186

body: { input: 'test' }

187

});

188

189

if (error) {

190

if (error instanceof FunctionsHttpError) {

191

console.error(`HTTP Error ${error.status}: ${error.statusText}`);

192

console.error('Message:', error.message);

193

194

// Handle specific HTTP status codes

195

switch (error.status) {

196

case 400:

197

console.error('Bad request - check your payload');

198

break;

199

case 401:

200

console.error('Unauthorized - check authentication');

201

break;

202

case 403:

203

console.error('Forbidden - insufficient permissions');

204

break;

205

case 404:

206

console.error('Function not found');

207

break;

208

case 500:

209

console.error('Internal server error in function');

210

break;

211

default:

212

console.error('Other HTTP error');

213

}

214

} else if (error instanceof FunctionsFetchError) {

215

console.error('Network error:', error.message);

216

// Handle network issues, retries, etc.

217

} else if (error instanceof FunctionsRelayError) {

218

console.error('Relay error:', error.message);

219

// Handle connection issues

220

} else {

221

console.error('Unknown error:', error.message);

222

}

223

}

224

```

225

226

## Advanced Function Patterns

227

228

### Authentication Integration

229

230

```typescript

231

// Invoke function with user authentication

232

const invokeWithAuth = async (functionName: string, payload: any) => {

233

// Get current session

234

const { data: { session }, error: sessionError } = await supabase.auth.getSession();

235

236

if (sessionError || !session) {

237

console.error('No active session');

238

return { data: null, error: new Error('Authentication required') };

239

}

240

241

// Invoke function with auth token

242

return supabase.functions.invoke(functionName, {

243

headers: {

244

'Authorization': `Bearer ${session.access_token}`

245

},

246

body: payload

247

});

248

};

249

250

// Usage

251

const { data, error } = await invokeWithAuth('user-specific-function', {

252

action: 'get_user_data'

253

});

254

```

255

256

### Retry Logic

257

258

```typescript

259

// Function invocation with retry logic

260

const invokeWithRetry = async (

261

functionName: string,

262

options: FunctionInvokeOptions,

263

maxRetries: number = 3,

264

delay: number = 1000

265

): Promise<FunctionResponse<any>> => {

266

let lastError: FunctionsError | null = null;

267

268

for (let attempt = 1; attempt <= maxRetries; attempt++) {

269

try {

270

const { data, error } = await supabase.functions.invoke(functionName, options);

271

272

if (!error) {

273

return { data, error: null };

274

}

275

276

lastError = error;

277

278

// Only retry on network errors or 5xx status codes

279

if (error instanceof FunctionsHttpError && error.status < 500) {

280

break; // Don't retry client errors

281

}

282

283

if (attempt < maxRetries) {

284

console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);

285

await new Promise(resolve => setTimeout(resolve, delay));

286

delay *= 2; // Exponential backoff

287

}

288

} catch (err) {

289

lastError = err as FunctionsError;

290

if (attempt < maxRetries) {

291

await new Promise(resolve => setTimeout(resolve, delay));

292

delay *= 2;

293

}

294

}

295

}

296

297

return { data: null, error: lastError };

298

};

299

300

// Usage

301

const { data, error } = await invokeWithRetry(

302

'unreliable-function',

303

{ body: { data: 'test' } },

304

3,

305

1000

306

);

307

```

308

309

### Batch Function Execution

310

311

```typescript

312

// Execute multiple functions concurrently

313

const executeBatch = async (functionCalls: Array<{

314

name: string;

315

options?: FunctionInvokeOptions;

316

}>) => {

317

const promises = functionCalls.map(call =>

318

supabase.functions.invoke(call.name, call.options)

319

);

320

321

const results = await Promise.allSettled(promises);

322

323

return results.map((result, index) => ({

324

functionName: functionCalls[index].name,

325

success: result.status === 'fulfilled' && !result.value.error,

326

data: result.status === 'fulfilled' ? result.value.data : null,

327

error: result.status === 'fulfilled' ? result.value.error : result.reason

328

}));

329

};

330

331

// Usage

332

const results = await executeBatch([

333

{ name: 'function-1', options: { body: { id: 1 } } },

334

{ name: 'function-2', options: { body: { id: 2 } } },

335

{ name: 'function-3', options: { body: { id: 3 } } }

336

]);

337

338

results.forEach((result, index) => {

339

if (result.success) {

340

console.log(`Function ${result.functionName} succeeded:`, result.data);

341

} else {

342

console.error(`Function ${result.functionName} failed:`, result.error);

343

}

344

});

345

```

346

347

### Streaming Responses

348

349

```typescript

350

// Handle streaming responses (if your function returns streaming data)

351

const handleStreamingFunction = async (functionName: string, payload: any) => {

352

try {

353

const { data, error } = await supabase.functions.invoke(functionName, {

354

body: payload,

355

headers: {

356

'Accept': 'text/stream'

357

}

358

});

359

360

if (error) {

361

console.error('Streaming function error:', error);

362

return;

363

}

364

365

// Handle streaming data (implementation depends on function response format)

366

if (typeof data === 'string') {

367

const lines = data.split('\n');

368

lines.forEach(line => {

369

if (line.trim()) {

370

try {

371

const jsonData = JSON.parse(line);

372

console.log('Streamed data:', jsonData);

373

} catch {

374

console.log('Streamed text:', line);

375

}

376

}

377

});

378

}

379

} catch (err) {

380

console.error('Streaming error:', err);

381

}

382

};

383

```

384

385

### Function Response Caching

386

387

```typescript

388

// Simple in-memory cache for function responses

389

class FunctionCache {

390

private cache = new Map<string, { data: any; timestamp: number; ttl: number }>();

391

392

async invoke<T>(

393

functionName: string,

394

options: FunctionInvokeOptions = {},

395

cacheTTL: number = 300000 // 5 minutes default

396

): Promise<FunctionResponse<T>> {

397

const cacheKey = this.getCacheKey(functionName, options);

398

const cached = this.cache.get(cacheKey);

399

400

// Return cached response if still valid

401

if (cached && Date.now() - cached.timestamp < cached.ttl) {

402

console.log('Returning cached response for', functionName);

403

return { data: cached.data, error: null };

404

}

405

406

// Invoke function

407

const { data, error } = await supabase.functions.invoke<T>(functionName, options);

408

409

// Cache successful responses

410

if (!error && data !== null) {

411

this.cache.set(cacheKey, {

412

data,

413

timestamp: Date.now(),

414

ttl: cacheTTL

415

});

416

}

417

418

return { data, error };

419

}

420

421

private getCacheKey(functionName: string, options: FunctionInvokeOptions): string {

422

return `${functionName}:${JSON.stringify(options)}`;

423

}

424

425

clearCache(): void {

426

this.cache.clear();

427

}

428

429

clearExpired(): void {

430

const now = Date.now();

431

for (const [key, value] of this.cache.entries()) {

432

if (now - value.timestamp >= value.ttl) {

433

this.cache.delete(key);

434

}

435

}

436

}

437

}

438

439

// Usage

440

const functionCache = new FunctionCache();

441

442

const { data, error } = await functionCache.invoke(

443

'expensive-calculation',

444

{ body: { input: 'complex-data' } },

445

600000 // Cache for 10 minutes

446

);

447

```

448

449

### Function Development Integration

450

451

```typescript

452

// Helper for local development with Edge Functions

453

const isDevelopment = process.env.NODE_ENV === 'development';

454

const EDGE_FUNCTION_URL = isDevelopment

455

? 'http://localhost:54321/functions/v1'

456

: undefined;

457

458

// Create client with local functions URL for development

459

const supabaseClient = isDevelopment

460

? createClient(supabaseUrl, supabaseKey, {

461

global: {

462

headers: {

463

'x-forwarded-host': 'localhost:3000'

464

}

465

}

466

})

467

: supabase;

468

469

// Function invocation wrapper that logs in development

470

const invokeFunction = async (functionName: string, options?: FunctionInvokeOptions) => {

471

if (isDevelopment) {

472

console.log(`Invoking function: ${functionName}`, options);

473

}

474

475

const startTime = Date.now();

476

const { data, error } = await supabaseClient.functions.invoke(functionName, options);

477

const duration = Date.now() - startTime;

478

479

if (isDevelopment) {

480

console.log(`Function ${functionName} completed in ${duration}ms`);

481

if (error) {

482

console.error('Function error:', error);

483

} else {

484

console.log('Function response:', data);

485

}

486

}

487

488

return { data, error };

489

};

490

```