Supabase Edge Functions: Server-Side Logic Without a Server

· 10 min read · Backend Development

Using Supabase Edge Functions for webhook processing, database triggers, and server-side logic without maintaining a backend server.

Supabase Edge Functions: Server-Side Logic Without a Server

You need server-side logic — webhook processing, third-party API calls, or data transformations — but you do not want to set up and maintain a backend server. Supabase Edge Functions give you serverless compute that runs close to your database.

What Edge Functions Are

Edge Functions are Deno-based serverless functions deployed to Supabase infrastructure. They run on the edge (close to users) and have direct access to your Supabase project.

Key characteristics:

  • Written in TypeScript or JavaScript
  • Run on Deno runtime
  • Cold starts under 200ms
  • Direct access to Supabase client libraries
  • Triggered via HTTP, database webhooks, or cron

Basic Function

// supabase/functions/hello-world/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";

serve(async (req) => {
  const { name } = await req.json();

  return new Response(
    JSON.stringify({ message: `Hello, ${name}!` }),
    { headers: { "Content-Type": "application/json" } },
  );
});

Deploy:

supabase functions deploy hello-world

Invoke:

curl -X POST https://your-project.supabase.co/functions/v1/hello-world \
  -H "Authorization: Bearer YOUR_ANON_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "World"}'

Real Use Case: Webhook Processing

Process Stripe webhooks with payment verification:

import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
import Stripe from "https://esm.sh/stripe@14";

const stripe = new Stripe(Deno.env.get("STRIPE_SECRET_KEY")!, {
  apiVersion: "2024-04-10",
});

serve(async (req) => {
  const signature = req.headers.get("stripe-signature")!;
  const body = await req.text();

  const event = stripe.webhooks.constructEvent(
    body,
    signature,
    Deno.env.get("STRIPE_WEBHOOK_SECRET")!,
  );

  const supabase = createClient(
    Deno.env.get("SUPABASE_URL")!,
    Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
  );

  switch (event.type) {
    case "checkout.session.completed": {
      const session = event.data.object;
      await supabase
        .from("subscriptions")
        .upsert({
          user_id: session.metadata?.user_id,
          status: "active",
          stripe_customer_id: session.customer,
          current_period_end: new Date(session.expires_at! * 1000).toISOString(),
        });
      break;
    }
    case "customer.subscription.deleted": {
      const subscription = event.data.object;
      await supabase
        .from("subscriptions")
        .update({ status: "canceled" })
        .eq("stripe_customer_id", subscription.customer);
      break;
    }
  }

  return new Response(JSON.stringify({ received: true }), { status: 200 });
});

Database Webhooks

Trigger a function when data changes:

-- In Supabase Dashboard → Database → Webhooks
-- Or via SQL:
CREATE TRIGGER on_new_user
  AFTER INSERT ON auth.users
  FOR EACH ROW
  EXECUTE FUNCTION supabase_functions.http_request(
    'https://your-project.supabase.co/functions/v1/on-new-user'