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

  1. Open Dashboard → your project → Functions tab and click New function.
  2. 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.
  3. 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:

  1. Pick shopping-list, method POST, and as anon key.
  2. Set the body to { "ingredients": ["Tomato", "pasta", "butter", "saffron"] }.
  3. 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

Updated 11 November 2026 · View as Markdown · Something wrong or missing? Tell us