Supabase Edge Functions are Deno-based serverless functions that run at the edge — close to your users, with access to your Supabase database and auth context. They're the answer to 'I need a bit of server-side logic but I don't want to run a separate backend service.'
When to Use Edge Functions
- Sending transactional emails triggered by database events
- Processing webhooks from Stripe, GitHub, or other services
- Running server-side AI logic that needs access to your database
- Custom auth logic beyond what Supabase Auth provides natively
- Calling third-party APIs with secrets that can't be exposed client-side
Creating Your First Edge Function
# Install Supabase CLI
npm install -g supabase
# Initialise (if not already done)
supabase init
# Create a new edge function
supabase functions new process-document
# This creates: supabase/functions/process-document/index.ts// supabase/functions/process-document/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 { document_id, content } = await req.json();
// Access Supabase with service role key (available as env var)
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
);
// Call OpenAI
const embeddingRes = await fetch('https://api.openai.com/v1/embeddings', {
method: 'POST',
headers: {
'Authorization': `Bearer ${Deno.env.get('OPENAI_API_KEY')}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ model: 'text-embedding-3-small', input: content }),
});
const { data } = await embeddingRes.json();
const embedding = data[0].embedding;
// Save to database
await supabase
.from('documents')
.update({ embedding })
.eq('id', document_id);
return new Response(JSON.stringify({ success: true }), {
headers: { 'Content-Type': 'application/json' },
});
});Setting Secrets
# Set secrets for your edge functions
supabase secrets set OPENAI_API_KEY=sk-...
# List current secrets
supabase secrets listDeploying
# Deploy a specific function
supabase functions deploy process-document
# Deploy all functions
supabase functions deploy
# The function URL is:
# https://[project-ref].supabase.co/functions/v1/process-documentCalling Edge Functions from Your App
// From a Next.js server component or API route
const { data, error } = await supabase.functions.invoke('process-document', {
body: { document_id: '123', content: 'text to embed...' },
});
// The Supabase client automatically sends the user's JWT
// so your function can verify authenticationRecipe: Stripe Webhook Handler
// supabase/functions/stripe-webhook/index.ts
import Stripe from 'https://esm.sh/stripe@13';
const stripe = new Stripe(Deno.env.get('STRIPE_SECRET_KEY')!, {
apiVersion: '2023-10-16',
httpClient: Stripe.createFetchHttpClient(),
});
serve(async (req) => {
const signature = req.headers.get('stripe-signature')!;
const body = await req.text();
const event = stripe.webhooks.constructEvent(
body, signature, Deno.env.get('STRIPE_WEBHOOK_SECRET')!
);
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
await activateSubscription(session.customer_email);
}
return new Response(JSON.stringify({ received: true }));
});Local Development
# Serve functions locally (requires Docker)
supabase start
supabase functions serve
# Test with curl
curl -i --location --request POST \
'http://127.0.0.1:54321/functions/v1/process-document' \
--header 'Authorization: Bearer [anon-key]' \
--header 'Content-Type: application/json' \
--data '{"document_id":"1","content":"test"}'| Metadata | Value |
|---|---|
| Title | Supabase Edge Functions: Server-Side Logic Without a Separate Backend |
| Tool | Supabase |
| Primary SEO keyword | supabase edge functions |
| Secondary keywords | supabase edge functions deno, supabase serverless, supabase webhook, supabase openai |
| Estimated read time | 8 minutes |
| Research date | 2026-04-14 |