Edge Functions Overview
What Are Edge Functions?
Edge Functions are serverless functions that run on the edge—geographically close to your users. They execute custom code in response to HTTP requests, enabling server-side logic without managing servers.
Why Edge Functions?
While Supabase provides powerful database features, some tasks require custom server-side code:
- Third-party API integration: Stripe payments, email services, external APIs
- Custom business logic: Complex calculations, workflows
- Secure operations: API keys that can't be exposed to clients
- Data processing: Transform data before/after database operations
- Webhooks: Receive and process external events
Edge vs Traditional Serverless
Traditional Serverless (AWS Lambda, Cloud Functions)
User in Tokyo ──────────────────────────────────→ Function in Virginia
High latency (~200ms)
Edge Functions
User in Tokyo ────────→ Edge Function in Tokyo
Low latency (~20ms)
User in Paris ────────→ Edge Function in Paris
Low latency (~20ms)
Edge Functions deploy to multiple locations worldwide, reducing latency for global users.
Supabase Edge Functions Architecture
┌─────────────────────────────────────────────────────────────┐
│ Client Application │
└─────────────────────────────────────────────────────────────┘
│
HTTPS Request
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Edge Network (Deno Deploy) │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Your Edge Function │ │
│ │ │ │
│ │ - Runs in Deno runtime │ │
│ │ - TypeScript/JavaScript │ │
│ │ - Isolated execution environment │ │
│ │ - Access to Supabase client │ │
│ │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
│
Can connect to:
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Supabase │ │ External │ │ Other │
│ Database │ │ APIs │ │ Services │
└──────────┘ └──────────┘ └──────────┘
The Deno Runtime
Supabase Edge Functions use Deno, a modern JavaScript/TypeScript runtime:
Key Features
| Feature | Benefit |
|---|---|
| Native TypeScript | No compilation step needed |
| Secure by default | Explicit permission model |
| Web Standards | fetch, Request, Response work natively |
| URL imports | No package.json needed |
| Fast startup | Quick cold starts |
Differences from Node.js
// Node.js style (doesn't work in Deno)
const express = require('express')
const fs = require('fs')
// Deno style
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
// Web standard fetch (works natively)
const response = await fetch('https://api.example.com/data')
Basic Function Structure
Every Edge Function follows this pattern:
// supabase/functions/my-function/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
serve(async (req: Request) => {
// 1. Parse request
const { name } = await req.json()
// 2. Process logic
const greeting = `Hello, ${name}!`
// 3. Return response
return new Response(
JSON.stringify({ message: greeting }),
{
headers: { "Content-Type": "application/json" },
status: 200
}
)
})
Function File Structure
your-project/
├── supabase/
│ ├── functions/
│ │ ├── hello-world/
│ │ │ └── index.ts
│ │ ├── process-payment/
│ │ │ └── index.ts
│ │ └── send-email/
│ │ └── index.ts
│ └── config.toml
└── ...
Each function:
- Lives in its own directory
- Has an
index.tsentry point - Can have additional files/modules
- Shares environment variables
Invoking Edge Functions
From JavaScript Client
const { data, error } = await supabase.functions.invoke('hello-world', {
body: { name: 'Alice' }
})
console.log(data.message) // "Hello, Alice!"
Via HTTP
curl -X POST \
'https://your-project.supabase.co/functions/v1/hello-world' \
-H 'Authorization: Bearer YOUR_ANON_KEY' \
-H 'Content-Type: application/json' \
-d '{"name": "Alice"}'
Authentication in Edge Functions
Accessing User Context
The JWT is passed in the Authorization header:
import { serve } from "https://deno.land/std@0.168.0/http/server.ts"
import { createClient } from "https://esm.sh/@supabase/supabase-js@2"
serve(async (req: Request) => {
// Get auth header
const authHeader = req.headers.get('Authorization')!
// Create Supabase client with user's token
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_ANON_KEY')!,
{ global: { headers: { Authorization: authHeader } } }
)
// Get authenticated user
const { data: { user } } = await supabase.auth.getUser()
if (!user) {
return new Response('Unauthorized', { status: 401 })
}
// Now `user` contains the authenticated user's info
// RLS policies apply to database queries
const { data } = await supabase
.from('posts')
.select('*')
return new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json' }
})
})
Using Service Role for Admin Operations
// Service role bypasses RLS
const adminSupabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)
// This query ignores RLS policies
const { data } = await adminSupabase
.from('users')
.select('*')
Environment Variables
Setting Variables
Via CLI:
supabase secrets set MY_API_KEY=abc123
supabase secrets set STRIPE_SECRET_KEY=sk_live_...
Accessing Variables
const apiKey = Deno.env.get('MY_API_KEY')
const stripeKey = Deno.env.get('STRIPE_SECRET_KEY')
Built-in Variables
| Variable | Description |
|---|---|
SUPABASE_URL | Your project URL |
SUPABASE_ANON_KEY | Public anon key |
SUPABASE_SERVICE_ROLE_KEY | Admin key (secret!) |
Error Handling
serve(async (req: Request) => {
try {
const body = await req.json()
// Validate input
if (!body.email) {
return new Response(
JSON.stringify({ error: 'Email is required' }),
{ status: 400, headers: { 'Content-Type': 'application/json' } }
)
}
// Process...
const result = await processEmail(body.email)
return new Response(
JSON.stringify(result),
{ status: 200, headers: { 'Content-Type': 'application/json' } }
)
} catch (error) {
console.error('Function error:', error)
return new Response(
JSON.stringify({ error: 'Internal server error' }),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
)
}
})
Key Takeaways
- Edge = Low Latency: Functions run close to users
- Deno Runtime: TypeScript native, secure by default
- HTTP-triggered: Standard Request/Response model
- Auth-aware: JWT passed through, RLS applies
- Environment secrets: Secure API keys and credentials
- Supabase integrated: Easy database access from functions
Looking Ahead
Now that you understand what Edge Functions are, we'll explore the Deno runtime environment in detail.
Edge Functions bridge the gap between serverless convenience and edge performance. When you need custom logic that runs fast everywhere, they're your answer.

