A production guide to Supabase Edge Functions

Define function contracts, JWT and CORS boundaries, secrets, local parity, database consistency, observability, deployment, and rollback.

An Edge Function is a good boundary for a short request that needs a secret, a third-party API, or controlled privilege. It is not a reason to move every backend concern into one index.ts . Production design begins with the function contract, identity model, consistency requirement, and failure behavior, then chooses a runtime. Long work, large batches, or persistent connections may belong in a queue and worker instead of a request-bound function. As of 2026-07-29, Supabase describes an Edge Function request entering a gateway before a Deno-compatible TypeScript runtime executes it. The official overview notes that cold starts can occur and recommends short-lived, idempotent operations, with secrets supplied through project environment variables. Local development uses the Supabase CLI Edge Runtime to reduce the gap between local and production behavior. Implementation steps Write the contract first: method, path, authentication, input schema, maximum payload, success and error response, timeout, side effects, and idempotency key. Separate user-authenticated, server-to-server, and public-but-signed webhook endpoints. A single function with verify_jwt=false should not serve both public traffic and member operations. Isolate directories by function, keep shared code in _shared , and put tests outside the deployed handler. The official development guide uses the CLI to start the local stack and serve one or all functions. External responses need stable error codes and must not leak provider responses or stacks. supabase/ config.toml functions/ _shared/ cors.ts errors.ts create-report/ index.ts create-report-test/ index.test.ts Configure identity next. A normal user function keeps default JWT verification and derives the caller from Authorization; data access still relies on RLS. Only an endpoint such as a Stripe webhook, which has no Supabase user token but has an independent signature, should disable the gate precisely in config.toml : [functions.create-report] verify_jwt = true [functions.signed-webhook] verify_jwt = false Disabling gateway JWT verification does not make verification optional. The handler validates an external signature, timestamp or replay window, or server credential before any side effect. CORS is not authentication. Return an exact allowlisted origin, add Vary: Origin , and never use wildcard * with credentialed responses. Manage secrets through Supabase project secrets in production, never through committed .env files. Use a separate ignored .env.local with test credentials locally. Read only the variables a function needs, validate them at startup, and fail closed when a required value is absent. A log may name the missing variable but must not print its value. Make database writes consistent. User-scoped queries should use a caller-token client so RLS applies; reserve server secrets for legitimate administrative actions. Put multi-table transitions in a transaction or narrow RPC rather than three sequential REST calls. Protect external mutations with an idempotency key or outbox so a client retry does not create a second resource after an ambiguous timeout. Add runtime validation. A TypeScript interface does not make inbound HTTP JSON trusted. Validate content type, JSON shape, string lengths, enums, URL or ID formats, and nested array limits. Return 400 for invalid input, 401 for missing identity, 403 for an identified caller without permission, and 409 for a conflict. Do not convert every exception into a 200 response. Build observability around data minimization. Generate or propagate a request ID and record the function version, duration bucket, status, provider request ID, and a non-personal error class. Never log Authorization, Cookie, API keys, full payment data, or user prompts. Alert on 5xx, latency, and retries using thresholds derived from a real baseline. Create a release gate. Pin dependencies and versions, run formatting, lint, unit, local integration, and negative security tests, then deploy a named function instead of accidentally deploying every experiment. Record the commit SHA, configuration, and secret names—not values. Send a side-effect-free health request, followed by a real read-only canary. Failure and recovery When local works but remote fails, compare Deno imports and lock data, environment variable names, configured entrypoint, JWT gate, and region or network restrictions. Use debug output only as needed and remove credential material before sharing it. A 401 indicates that gateway or handler authentication did not pass. A 403 generally means the caller was identified but denied. Never solve a 401 by globally adding --no-verify-jwt . Disable JWT for one function only when its contract is a signed webhook and prove the replacement signature check. After an external API timeout, determine whether the operation might have succeeded. Without idempotency, do not blindly replay a mutation. Query provider state or send it to human reconciliation. If the function wrote the database before failing, retain explicit pending or failed state and retry through a worker rather than deleting the evidence. For a deployment causing 5xx, redeploy the saved previous commit and configuration for the named function, or stop traffic using a feature flag. Secret rotation is separate from code rollback. If a credential leaked, rolling back code does not revoke the old credential; rotate it at Supabase or the provider. Verification commands Start the local stack and named function, then test methods, CORS, authentication, and invalid payloads: supabase start supabase functions serve create-report --env-file .env.local curl -i -X OPTIONS http://127.0.0.1:54321/functions/v1/create-report \ -H 'Origin: https://allowed.example' curl -i -X POST http://127.0.0.1:54321/functions/v1/create-report \ -H 'Content-Type: application/json' -d '{}' The acceptance matrix includes no token, expired token, another user, invalid origin, wrong method, i