Documentation Index Fetch the complete documentation index at: https://mintlify.com/supabase/supabase/llms.txt
Use this file to discover all available pages before exploring further.
Supabase Edge Functions are server-side TypeScript functions distributed globally at the edge—close to your users. Built on Deno, they provide a portable, TypeScript-first runtime for custom backend logic, webhooks, and API integrations.
Architecture
Edge Functions run on a globally distributed infrastructure:
Kong Gateway : Routes requests and validates authentication
Edge Runtime : Deno-based runtime executing your function code
Regional Distribution : Functions deploy to multiple regions for low latency
Observability : Automatic logging and metrics collection
How Edge Functions Work
When a request arrives:
Kong gateway receives the HTTP request
Gateway validates authentication headers and applies rate limits
Request routes to the nearest Edge Runtime node
Edge Runtime executes your function
Function can access Supabase services (Auth, Database, Storage)
Response returns through the gateway to the client
Creating Functions
Basic Function
Create a simple HTTP endpoint:
// supabase/functions/hello-world/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
serve ( async ( req ) => {
const { name } = await req . json ()
return new Response (
JSON . stringify ({ message: `Hello ${ name } !` }),
{ headers: { 'Content-Type' : 'application/json' } }
)
})
Deploy the function:
supabase functions deploy hello-world
Invoke from your app:
const { data , error } = await supabase . functions . invoke ( 'hello-world' , {
body: { name: 'World' }
})
console . log ( data ) // { message: 'Hello World!' }
With Supabase Client
Access Supabase services from your function:
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 ) => {
// Create authenticated Supabase client
const supabase = createClient (
Deno . env . get ( 'SUPABASE_URL' ) ?? '' ,
Deno . env . get ( 'SUPABASE_ANON_KEY' ) ?? '' ,
{
global: {
headers: { Authorization: req . headers . get ( 'Authorization' ) ! }
}
}
)
// Get authenticated user
const { data : { user }, error : authError } = await supabase . auth . getUser ()
if ( authError || ! user ) {
return new Response ( 'Unauthorized' , { status: 401 })
}
// Query database
const { data : posts , error } = await supabase
. from ( 'posts' )
. select ( '*' )
. eq ( 'author_id' , user . id )
return new Response (
JSON . stringify ({ posts }),
{ headers: { 'Content-Type' : 'application/json' } }
)
})
With Database Connection
Connect directly to Postgres:
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
import { Pool } from 'https://deno.land/x/postgres@v0.17.0/mod.ts'
const pool = new Pool ({
hostname: Deno . env . get ( 'SUPABASE_DB_HOST' ),
port: 5432 ,
user: 'postgres' ,
password: Deno . env . get ( 'SUPABASE_DB_PASSWORD' ),
database: 'postgres' ,
tls: { enabled: true }
}, 3 ) // Max 3 connections
serve ( async ( req ) => {
const client = await pool . connect ()
try {
const result = await client . queryObject `
SELECT * FROM posts ORDER BY created_at DESC LIMIT 10
`
return new Response (
JSON . stringify ( result . rows ),
{ headers: { 'Content-Type' : 'application/json' } }
)
} finally {
client . release ()
}
})
Common Use Cases
Webhook Handler
// supabase/functions/stripe-webhook/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
import Stripe from 'https://esm.sh/stripe@14.0.0'
const stripe = new Stripe ( Deno . env . get ( 'STRIPE_SECRET_KEY' ) ! , {
apiVersion: '2023-10-16'
})
serve ( async ( req ) => {
const signature = req . headers . get ( 'stripe-signature' ) !
const body = await req . text ()
try {
const event = stripe . webhooks . constructEvent (
body ,
signature ,
Deno . env . get ( 'STRIPE_WEBHOOK_SECRET' ) !
)
if ( event . type === 'checkout.session.completed' ) {
const session = event . data . object
// Handle successful payment
console . log ( 'Payment successful:' , session . id )
}
return new Response ( JSON . stringify ({ received: true }), {
headers: { 'Content-Type' : 'application/json' }
})
} catch ( err ) {
return new Response ( `Webhook Error: ${ err . message } ` , { status: 400 })
}
})
AI/ML Integration
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
import OpenAI from 'https://deno.land/x/openai@v4.20.1/mod.ts'
const openai = new OpenAI ({
apiKey: Deno . env . get ( 'OPENAI_API_KEY' )
})
serve ( async ( req ) => {
const { prompt } = await req . json ()
const completion = await openai . chat . completions . create ({
model: 'gpt-4' ,
messages: [{ role: 'user' , content: prompt }]
})
return new Response (
JSON . stringify ({
response: completion . choices [ 0 ]. message . content
}),
{ headers: { 'Content-Type' : 'application/json' } }
)
})
Image Processing
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 ) => {
const supabase = createClient (
Deno . env . get ( 'SUPABASE_URL' ) ! ,
Deno . env . get ( 'SUPABASE_SERVICE_ROLE_KEY' ) !
)
const { imagePath } = await req . json ()
// Download image from Storage
const { data : imageData } = await supabase . storage
. from ( 'images' )
. download ( imagePath )
// Process image (example: convert to grayscale)
// ... image processing logic ...
// Upload processed image
const { data , error } = await supabase . storage
. from ( 'images' )
. upload ( `processed/ ${ imagePath } ` , processedImage )
return new Response (
JSON . stringify ({ path: data ?. path }),
{ headers: { 'Content-Type' : 'application/json' } }
)
})
Scheduled Tasks (with pg_cron)
-- Schedule a function to run every hour
select cron . schedule (
'cleanup-old-records' ,
'0 * * * *' , -- Every hour at minute 0
$$
select net . http_post (
url : = 'https://your-project.supabase.co/functions/v1/cleanup' ,
headers : = jsonb_build_object( 'Authorization' , 'Bearer ' || 'YOUR_SERVICE_ROLE_KEY' )
);
$$
);
// supabase/functions/cleanup/index.ts
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 ) => {
const supabase = createClient (
Deno . env . get ( 'SUPABASE_URL' ) ! ,
Deno . env . get ( 'SUPABASE_SERVICE_ROLE_KEY' ) !
)
// Delete records older than 30 days
const thirtyDaysAgo = new Date ()
thirtyDaysAgo . setDate ( thirtyDaysAgo . getDate () - 30 )
const { data , error } = await supabase
. from ( 'temporary_data' )
. delete ()
. lt ( 'created_at' , thirtyDaysAgo . toISOString ())
return new Response (
JSON . stringify ({ deleted: data ?. length ?? 0 }),
{ headers: { 'Content-Type' : 'application/json' } }
)
})
Environment Variables
Setting Secrets
# Set a secret
supabase secrets set OPENAI_API_KEY=sk-...
# Set multiple secrets from .env file
supabase secrets set --env-file .env.production
# List secrets (values are hidden)
supabase secrets list
# Unset a secret
supabase secrets unset OPENAI_API_KEY
Accessing in Functions
const apiKey = Deno . env . get ( 'OPENAI_API_KEY' )
const supabaseUrl = Deno . env . get ( 'SUPABASE_URL' )
const serviceRoleKey = Deno . env . get ( 'SUPABASE_SERVICE_ROLE_KEY' )
Default Environment Variables
These are automatically available:
SUPABASE_URL: Your project URL
SUPABASE_ANON_KEY: Anonymous key for client operations
SUPABASE_SERVICE_ROLE_KEY: Service role key for admin operations
SUPABASE_DB_URL: Database connection string
CORS Configuration
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
const corsHeaders = {
'Access-Control-Allow-Origin' : '*' ,
'Access-Control-Allow-Headers' : 'authorization, x-client-info, apikey, content-type'
}
serve ( async ( req ) => {
// Handle CORS preflight requests
if ( req . method === 'OPTIONS' ) {
return new Response ( 'ok' , { headers: corsHeaders })
}
try {
// Your function logic here
const data = { message: 'Hello!' }
return new Response (
JSON . stringify ( data ),
{
headers: {
... corsHeaders ,
'Content-Type' : 'application/json'
}
}
)
} catch ( error ) {
return new Response (
JSON . stringify ({ error: error . message }),
{
status: 400 ,
headers: {
... corsHeaders ,
'Content-Type' : 'application/json'
}
}
)
}
})
Local Development
Serve Locally
# Start all functions
supabase functions serve
# Start specific function
supabase functions serve hello-world --env-file .env.local
# With debugging
supabase functions serve --debug
Test Functions
# Using curl
curl -i --location --request POST 'http://localhost:54321/functions/v1/hello-world' \
--header 'Authorization: Bearer YOUR_ANON_KEY' \
--header 'Content-Type: application/json' \
--data '{"name":"World"}'
# Using Supabase client
const { data } = await supabase.functions.invoke ( 'hello-world' , {
body: { name: 'World' }
})
Background Tasks
For long-running operations, use background tasks:
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
serve ( async ( req ) => {
const { task } = await req . json ()
// Return immediately
const response = new Response (
JSON . stringify ({ task_id: crypto . randomUUID () }),
{ headers: { 'Content-Type' : 'application/json' } }
)
// Process in background
processTaskInBackground ( task )
return response
})
async function processTaskInBackground ( task : any ) {
// Long-running operation
await new Promise ( resolve => setTimeout ( resolve , 10000 ))
// Update status in database
const supabase = createClient (
Deno . env . get ( 'SUPABASE_URL' ) ! ,
Deno . env . get ( 'SUPABASE_SERVICE_ROLE_KEY' ) !
)
await supabase
. from ( 'tasks' )
. update ({ status: 'completed' })
. eq ( 'id' , task . id )
}
Monitoring and Debugging
Logging
serve ( async ( req ) => {
console . log ( 'Request received:' , {
method: req . method ,
url: req . url ,
headers: Object . fromEntries ( req . headers )
})
try {
// Function logic
console . log ( 'Processing...' )
return new Response ( 'Success' )
} catch ( error ) {
console . error ( 'Error:' , error )
return new Response ( 'Error' , { status: 500 })
}
})
Error Handling
serve ( async ( req ) => {
try {
const { data } = await req . json ()
if ( ! data ) {
throw new Error ( 'Missing data' )
}
// Process data
return new Response ( JSON . stringify ({ success: true }))
} catch ( error ) {
return new Response (
JSON . stringify ({
error: error . message ,
stack: error . stack
}),
{
status: 400 ,
headers: { 'Content-Type' : 'application/json' }
}
)
}
})
Connection Pooling
// Reuse database connections
const pool = new Pool ( connectionConfig , 3 )
serve ( async ( req ) => {
const client = await pool . connect ()
try {
// Use connection
const result = await client . queryObject `SELECT * FROM posts`
return new Response ( JSON . stringify ( result . rows ))
} finally {
client . release () // Always release
}
})
Caching
const cache = new Map ()
serve ( async ( req ) => {
const cacheKey = new URL ( req . url ). pathname
if ( cache . has ( cacheKey )) {
return new Response ( cache . get ( cacheKey ))
}
const data = await fetchData ()
cache . set ( cacheKey , JSON . stringify ( data ))
return new Response ( JSON . stringify ( data ))
})
Best Practices
Keep Functions Small Design functions for specific tasks to improve maintainability.
Use Service Role Wisely Only use service role key when necessary; prefer user context.
Handle Errors Gracefully Always implement proper error handling and return meaningful messages.
Monitor Performance Use logging and metrics to track function performance.
Limitations
Execution time : 150 seconds maximum
Memory : 512MB per invocation
Cold starts : First request may be slower
No persistent storage : Use Database or Storage for persistence
Limited concurrency : Design for short-lived operations
Next Steps
Quickstart Get started with your first Edge Function
AI Models Integrate AI and ML models
Background Tasks Handle long-running operations
Examples Explore example functions