supabase-edge-functions
npx skills add https://github.com/heyflouai/ikf-central-dashboard --skill supabase-edge-functions
Agent 安装分布
Skill 文档
Supabase Edge Functions Best Practices
Comprehensive guide for Supabase Edge Functions development, debugging, and integration with database triggers.
Core Concepts
Authentication Patterns
Edge Functions support two authentication modes:
- JWT Verification (verify_jwt: true) – Default, validates Authorization header contains valid JWT
- Custom Auth (verify_jwt: false) – Function handles auth internally (API keys, webhooks)
Service Role vs Anon Key:
- Anon key (
eyJ..., ~219 chars): JWT for client-side, limited permissions via RLS - Service role key (
sb_secret_..., ~600 chars): Full admin access, bypasses RLS, NEVER expose to client
Database Trigger Integration
When calling Edge Functions from database triggers using pg_net:
SELECT net.http_post(
url := 'https://project.supabase.co/functions/v1/function-name',
headers := jsonb_build_object(
'Content-Type', 'application/json',
'Authorization', 'Bearer ' || v_service_role_key -- Must be SERVICE ROLE KEY
),
body := jsonb_build_object('run_id', p_run_id)
);
Critical: Database triggers MUST use service role key, not anon key.
Common Issues and Solutions
Issue 1: Auth Token Mismatch
Symptoms:
- Logs show: “Token prefix: eyJ… Expected prefix: sb_secret_…”
- Function returns 401 or auth errors
- Database trigger calls fail silently
Root Cause: Anon key stored instead of service role key in database config.
Solution:
- Get service role key from Dashboard > Settings > API > service_role
- Verify key starts with
sb_secret_and is ~600 characters - Update database config:
UPDATE private.config
SET value = 'sb_secret_YOUR_KEY_HERE'
WHERE key = 'service_role_key';
Verification:
SELECT
key,
LEFT(value, 10) as value_preview,
LENGTH(value) as key_length
FROM private.config
WHERE key = 'service_role_key';
-- Should show: sb_secret_... with length ~600
Issue 2: Function Deployment Errors
Common Errors:
- Import map not found
- Module resolution failures
- Type errors in Deno runtime
Solutions:
Import Maps:
Use deno.json for dependencies:
{
"imports": {
"supabase": "jsr:@supabase/supabase-js@2",
"postgres": "https://deno.land/x/postgres@v0.17.0/mod.ts"
}
}
Type Safety: Import runtime types at top of function:
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
Testing Locally:
# Install Supabase CLI
supabase functions serve function-name --env-file .env.local
# Test with curl
curl -i --location --request POST 'http://localhost:54321/functions/v1/function-name' \
--header 'Authorization: Bearer YOUR_ANON_KEY' \
--header 'Content-Type: application/json' \
--data '{"run_id":"test-uuid"}'
Issue 3: Database Connection from Edge Functions
Pattern:
import { createClient } from "jsr:@supabase/supabase-js@2";
Deno.serve(async (req: Request) => {
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')! // Service role for admin ops
);
// Now can bypass RLS for admin operations
const { data, error } = await supabase
.from('forecast_runs')
.update({ status: 'processing' })
.eq('id', runId);
});
Environment Variables: Edge Functions automatically have access to:
SUPABASE_URL– Project URLSUPABASE_ANON_KEY– Public anon keySUPABASE_SERVICE_ROLE_KEY– Admin service role key
Issue 4: Debugging Failed Triggers
Check trigger configuration:
SELECT * FROM private.config WHERE key IN ('supabase_url', 'service_role_key');
Check pg_net requests:
SELECT * FROM net._http_response ORDER BY created DESC LIMIT 10;
Check Edge Function logs:
Use Supabase MCP tool get_logs or Dashboard > Edge Functions > Logs
Enable verbose logging in function:
console.log('[function-name] Processing run_id:', runId);
console.log('[function-name] Request headers:', Object.fromEntries(req.headers));
console.log('[function-name] Environment check:', {
hasUrl: !!Deno.env.get('SUPABASE_URL'),
hasServiceKey: !!Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')
});
Deployment Workflow
1. Develop Locally
supabase functions serve function-name
2. Test Locally
curl -i --location --request POST 'http://localhost:54321/functions/v1/function-name' \
--header 'Authorization: Bearer ANON_KEY' \
--data '{"test": "data"}'
3. Deploy to Production
supabase functions deploy function-name
4. Verify Deployment
# Check function exists
supabase functions list
# Test production endpoint
curl -i --location --request POST 'https://PROJECT.supabase.co/functions/v1/function-name' \
--header 'Authorization: Bearer ANON_KEY' \
--data '{"test": "data"}'
5. Monitor Logs
# Via CLI
supabase functions logs function-name
# Via Dashboard
Dashboard > Edge Functions > function-name > Logs
File Structure Best Practices
supabase/functions/
âââ function-name/
â âââ index.ts # Main handler
â âââ deno.json # Import map
â âââ parsers/ # Domain logic (separate from handler)
â â âââ csv-parser.ts
â âââ _shared/ # Shared utilities (symlinked)
â âââ validation.ts
Shared Code:
Use _shared/ directory for code reused across functions. Supabase CLI automatically includes it.
Error Handling Pattern
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
Deno.serve(async (req: Request) => {
try {
// Parse request
const body = await req.json();
// Validate input
if (!body.run_id) {
return new Response(
JSON.stringify({ error: 'run_id required' }),
{ status: 400, headers: { 'Content-Type': 'application/json' } }
);
}
// Process
const result = await processData(body.run_id);
// Success response
return new Response(
JSON.stringify({ success: true, data: result }),
{ status: 200, headers: { 'Content-Type': 'application/json' } }
);
} catch (error) {
console.error('[function-name] Error:', error);
return new Response(
JSON.stringify({
error: error.message,
stack: error.stack // Include for debugging, remove in production
}),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
});
Security Best Practices
- Never expose service role key to client – Only use in Edge Functions or database
- Use RLS policies – Even with service role, validate permissions in function logic
- Validate all inputs – Never trust request data
- Rate limiting – Implement for public endpoints
- CORS configuration – Restrict origins in production
- Secrets management – Use Supabase secrets, not hardcoded values