Writing and Invoking a Function
The idea in one sentence
Section titled “The idea in one sentence”An Edge Function is a plain Deno.serve handler that receives an HTTP request and returns a response, invoked from a client with supabase.functions.invoke().
A minimal real function
Section titled “A minimal real function”Deno.serve is a global provided by the Deno runtime — there is nothing to import for it. You hand it a callback that receives the incoming Request and returns a Response, exactly like the Fetch API you already know from the browser:
Deno.serve(async (req) => { const { name } = await req.json() return new Response(JSON.stringify({ message: `Hello ${name}!` }), { headers: { 'Content-Type': 'application/json' }, })})This file lives at supabase/functions/hello-world/index.ts. Once deployed, it is reachable at https://<project-ref>.supabase.co/functions/v1/hello-world, but you rarely construct that URL by hand — the client library does it for you.
Invoking it from a client
Section titled “Invoking it from a client”supabase-js exposes a dedicated functions.invoke() method rather than a plain fetch, because it does useful work on your behalf:
const { data, error } = await supabase.functions.invoke('hello-world', { body: { name: 'World' },})Beyond serializing body to JSON, invoke() automatically attaches an Authorization header — the currently signed-in user’s access token if someone is logged in, or the project’s anon key otherwise. That means your function can inspect who called it (or confirm no one is authenticated) without you wiring that header manually on every call.
Secrets and environment variables
Section titled “Secrets and environment variables”A function frequently needs a credential it should never expose to the client — a third-party API key, a signing secret. Set one for your deployed functions with the CLI:
supabase secrets set MY_API_KEY=sk_live_examplekey123Read it inside the function with Deno.env.get:
const apiKey = Deno.env.get('MY_API_KEY')For local development, supabase functions serve reads a .env file placed under supabase/functions/, so the same Deno.env.get call works identically whether you are running locally or against the deployed function — only where the value comes from changes.
flowchart LR
client["supabase.functions.invoke('hello-world', { body })"] -->|"HTTP request + Authorization header"| edge["Deployed Deno function (Deno.serve)"]
edge -->|"Response"| client