Loading...
Loading...
Best practices for developing, deploying, and debugging Supabase Edge Functions (Deno runtime). Use when working with Edge Functions for tasks like ingest pipelines, webhooks, scheduled jobs, or database triggers. Covers authentication patterns (service role vs anon key), error debugging, database integration, and common pitfalls.
npx skill4agent add heyflouai/ikf-central-dashboard supabase-edge-functionseyJ...sb_secret_...pg_netSELECT 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)
);sb_secret_UPDATE private.config
SET value = 'sb_secret_YOUR_KEY_HERE'
WHERE key = 'service_role_key';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 ~600deno.json{
"imports": {
"supabase": "jsr:@supabase/supabase-js@2",
"postgres": "https://deno.land/x/postgres@v0.17.0/mod.ts"
}
}import "jsr:@supabase/functions-js/edge-runtime.d.ts";# 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"}'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);
});SUPABASE_URLSUPABASE_ANON_KEYSUPABASE_SERVICE_ROLE_KEYSELECT * FROM private.config WHERE key IN ('supabase_url', 'service_role_key');SELECT * FROM net._http_response ORDER BY created DESC LIMIT 10;get_logsconsole.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')
});supabase functions serve function-namecurl -i --location --request POST 'http://localhost:54321/functions/v1/function-name' \
--header 'Authorization: Bearer ANON_KEY' \
--data '{"test": "data"}'supabase functions deploy function-name# 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"}'# Via CLI
supabase functions logs function-name
# Via Dashboard
Dashboard > Edge Functions > function-name > Logssupabase/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/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' } }
);
}
});