Your first Edge Function
Create, deploy, test and call an Edge Function on SupaHost from the dashboard in a few minutes, then update it with a new version.
This walkthrough deploys a small function for a recipe app. It takes a list of ingredients and returns a shopping list sorted by aisle. You'll write it in the dashboard, deploy it, test it, and call it from your app. It takes about five minutes.
You need a SupaHost project and the owner role on the instance (only the owner can deploy).
1. Create the function
- Open Dashboard → your project → Functions tab and click New function.
- In Name, type
shopping-list. The hint under the field shows the URL it will get:https://app-one.acme.supahost.space/functions/v1/shopping-list. Names use lowercase letters, numbers,-and_, and you can't change a name after the first deploy. - Leave Require a JWT on. Callers will need to send your project's anon key or a signed-in user's access token.
The editor opens with a starter index.ts. Replace its contents with:
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};
const AISLES: Record<string, string> = {
tomato: "Produce",
basil: "Produce",
garlic: "Produce",
pasta: "Dry goods",
rice: "Dry goods",
parmesan: "Dairy",
butter: "Dairy",
};
Deno.serve(async (req) => {
// Browsers send a preflight request first. Answer it with the CORS headers.
if (req.method === "OPTIONS") {
return new Response("ok", { headers: corsHeaders });
}
const { ingredients } = await req.json().catch(() => ({ ingredients: null }));
if (!Array.isArray(ingredients)) {
return Response.json(
{ error: "Send a JSON body like { \"ingredients\": [\"tomato\", \"pasta\"] }" },
{ status: 400, headers: corsHeaders },
);
}
const list: Record<string, string[]> = {};
for (const item of ingredients.map((i: unknown) => String(i).toLowerCase().trim())) {
const aisle = AISLES[item] ?? "Other";
(list[aisle] ??= []).push(item);
}
console.log(`Built a list with ${ingredients.length} items`);
return Response.json({ list }, { headers: corsHeaders });
});
2. Deploy it
Click Deploy function. The row shows Deploying v1… while SupaHost builds it. The build resolves every import and checks the code, which takes about a minute. When it's done the status changes to Live · v1.
If the build fails, the row shows Deploy failed with the error underneath. Fix the code with Edit and deploy again. See Edge Function deploy failed for common causes.
3. Test it in the dashboard
Once a function is live, the Test a function card appears below the list:
- Pick
shopping-list, method POST, and as anon key. - Set the body to
{ "ingredients": ["Tomato", "pasta", "butter", "saffron"] }. - Click Send.
You'll see the status code, the time it took and the JSON response. The card sends a real request, waits up to 60 seconds, and shows the first 64 KB of the body.
The Logs card further down now shows a request line (POST /shopping-list → 200 in 12 ms) and the console.log message.
4. Call it from your app
With supabase-js, functions.invoke sends the right headers for you. If a user is signed in it sends their access token, otherwise the anon key:
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(
import.meta.env.VITE_SUPABASE_URL, // https://app-one.acme.supahost.space
import.meta.env.VITE_SUPABASE_ANON_KEY,
);
const { data, error } = await supabase.functions.invoke("shopping-list", {
body: { ingredients: ["tomato", "basil", "rice"] },
});
if (error) console.error(error);
else console.log(data.list); // { Produce: ["tomato", "basil"], "Dry goods": ["rice"] }
From a terminal:
curl -X POST "https://app-one.acme.supahost.space/functions/v1/shopping-list" \
-H "Authorization: Bearer $SUPABASE_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"ingredients":["garlic","parmesan"]}'
Leave out the Authorization header and you'll get a 401 with UNAUTHORIZED_NO_AUTH_HEADER, which shows the JWT check is working.
5. Ship a change
Click Edit on the row, change the code (add "onion": "Produce" to the map, say) and click Deploy new version. The row shows Deploying v2…. Version 1 keeps answering requests until version 2 is built, so there's no gap. If version 2 fails to build, version 1 stays live.
Deploying with an AI agent instead
If you've connected a coding agent through the SupaHost MCP server, you can ask it to deploy the function. It uses the deploy_edge_function tool with the same files, then checks list_edge_functions until the status is active. See Deploying functions.
Next steps
- Writing functions: Deno, imports and structure
- Authentication and JWT verification, to find out who is calling.
- Environment variables and secrets, to use API keys.
- Calling functions from your app