Aller au contenu
WorkflowPro
Code custom

Supabase Edge Functions : automatiser sans serveur en 2026

Guide complet pour automatiser avec Supabase Edge Functions : webhooks, cron, intégrations API. Sans serveur, sans DevOps, en Deno + TypeScript.

EA

Etienne Aubry

Développeur & Expert Automatisation IA

· · 9 min de lecture · 1683 mots
Code serverless sur écran sombre avec terminal
Code serverless sur écran sombre avec terminal

Quand un client me demande “tu peux me coder un petit truc qui envoie un mail quand X arrive ?”, je ne sors plus mon VPS, ni n8n, ni même AWS Lambda. Depuis 2024, ma réponse par défaut c’est Supabase Edge Functions. Gratuit jusqu’à 500K invocations/mois, déployable en une commande, et écrit en TypeScript moderne via Deno.

Sur 30 projets clients passés en revue cette année, j’ai migré 11 micro-automatisations de n8n ou de scripts Node hébergés sur un VPS vers Supabase. Résultat : 0 € de facture supplémentaire, 0 maintenance, et un code propre versionné dans Git. Voici le guide complet pour faire pareil.

Pourquoi Supabase Edge Functions plutôt qu’AWS Lambda ou Vercel

Trois raisons concrètes :

1. Le DX est imbattable. Tu écris supabase functions new ma-fonction, tu codes en local avec supabase functions serve, tu déploies avec supabase functions deploy. Trois commandes, zéro config IAM, zéro CloudFormation. Quand j’ai voulu faire le même setup sur AWS Lambda, il m’a fallu 2h pour configurer correctement les rôles, les policies, et CloudWatch. Sur Supabase, j’avais ma fonction en prod en 12 minutes chrono.

2. C’est lié à ta base de données. Si tu utilises déjà Supabase comme Postgres managé, tes Edge Functions ont accès direct à supabase-js avec le service role key déjà injecté. Pas besoin de gérer les connexions, le connection pooling, ou les secrets. Tu fais const { data } = await supabase.from('users').select('*') et ça marche.

3. Le runtime Deno est rapide. Cold start moyen : 50 à 150 ms (contre 300-800 ms pour Lambda Node). Sur des webhooks à fort trafic, ça change la vie. Et tu as accès à toutes les API Web standards (fetch, crypto.subtle, WebSocket) sans installer 200 dépendances.

Le seul vrai défaut : la limite de 150s par invocation (contre 15 min sur Lambda). Pour 95% des automatisations c’est largement suffisant, mais pour des tâches longues (traitement vidéo, ML), passe par autre chose ou par une architecture queue + worker.

Installation et premier projet

Pré-requis : un compte Supabase (gratuit, supabase.com) et la CLI installée. Sur Mac : brew install supabase/tap/supabase. Sur Windows : scoop install supabase. Sur Linux : télécharge le binaire depuis GitHub releases.

Ensuite :

supabase login
supabase init
supabase functions new send-welcome-email

Tu te retrouves avec une structure :

supabase/
  functions/
    send-welcome-email/
      index.ts
      deno.json

Le fichier index.ts contient un template minimal :

import { serve } from "https://deno.land/std@0.224.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" } }
  );
});

Pour tester en local : supabase functions serve send-welcome-email --no-verify-jwt. Tu peux appeler ta fonction sur http://localhost:54321/functions/v1/send-welcome-email. Le --no-verify-jwt désactive l’auth obligatoire en dev, on l’enlève en prod.

Cas d’usage 1 : recevoir un webhook Stripe et envoyer un email

C’est l’automatisation type. Stripe envoie un événement checkout.session.completed, tu veux remercier le client par mail via Resend ou Postmark, et stocker la commande en base.

Voici le code complet :

import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.45.0";
import Stripe from "https://esm.sh/stripe@14.21.0?target=denonext";

const stripe = new Stripe(Deno.env.get("STRIPE_SECRET_KEY")!, {
  apiVersion: "2024-06-20",
  httpClient: Stripe.createFetchHttpClient(),
});

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

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

  let event;
  try {
    event = await stripe.webhooks.constructEventAsync(
      body,
      signature!,
      Deno.env.get("STRIPE_WEBHOOK_SECRET")!,
      undefined,
      Stripe.createSubtleCryptoProvider()
    );
  } catch (err) {
    return new Response(`Webhook Error: ${err.message}`, { status: 400 });
  }

  if (event.type === "checkout.session.completed") {
    const session = event.data.object;

    await supabase.from("orders").insert({
      stripe_session_id: session.id,
      email: session.customer_email,
      amount: session.amount_total,
      status: "paid",
    });

    await fetch("https://api.resend.com/emails", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${Deno.env.get("RESEND_API_KEY")}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        from: "noreply@tonsite.fr",
        to: session.customer_email,
        subject: "Merci pour ta commande !",
        html: `<p>Hello, ta commande est confirmée. Total : ${(session.amount_total / 100).toFixed(2)} €</p>`,
      }),
    });
  }

  return new Response(JSON.stringify({ received: true }), {
    headers: { "Content-Type": "application/json" },
  });
});

Le truc important : constructEventAsync (et pas constructEvent), parce que Deno utilise Web Crypto en async. C’est la première erreur que tout le monde fait en migrant depuis Node.

Pour déployer :

supabase secrets set STRIPE_SECRET_KEY=sk_live_xxx
supabase secrets set STRIPE_WEBHOOK_SECRET=whsec_xxx
supabase secrets set RESEND_API_KEY=re_xxx
supabase functions deploy stripe-webhook --no-verify-jwt

Le --no-verify-jwt est obligatoire ici parce que Stripe ne va pas envoyer de JWT Supabase dans ses webhooks. La sécurité passe par la vérification de la signature stripe-signature.

Cas d’usage 2 : un cron qui tourne tous les jours

Supabase ne propose pas (encore) de scheduler natif intégré aux Edge Functions. Mais tu as deux options gratuites :

Option A : pg_cron sur ta base Postgres. Tu actives l’extension dans Database → Extensions, puis tu crées un job qui appelle ta fonction via net.http_post :

select cron.schedule(
  'daily-report',
  '0 8 * * *',
  $$
  select net.http_post(
    url := 'https://abc123.supabase.co/functions/v1/generate-report',
    headers := jsonb_build_object('Authorization', 'Bearer ' || current_setting('app.cron_secret'))
  );
  $$
);

Option B : GitHub Actions. Tu crées un workflow .github/workflows/cron.yml qui ping ta fonction toutes les X heures. Plus simple à débugger, mais dépendant de GitHub. C’est ce que j’utilise sur les projets clients où je n’ai pas envie de toucher à la base. Je détaille la comparaison dans Cron jobs vs n8n vs GitHub Actions.

Cas d’usage 3 : transformer un fichier uploadé

Supabase Storage déclenche des webhooks quand un fichier est uploadé. Tu peux brancher une Edge Function dessus pour le traiter (resize image, extraction PDF, transcription audio).

Exemple : générer une thumbnail à chaque upload dans le bucket avatars :

import { serve } from "https://deno.land/std@0.224.0/http/server.ts";
import { ImageMagick, initialize } from "https://deno.land/x/imagemagick_deno@0.0.26/mod.ts";

await initialize();

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

  // télécharger l'original
  const original = await fetch(
    `${Deno.env.get("SUPABASE_URL")}/storage/v1/object/avatars/${path}`,
    { headers: { Authorization: `Bearer ${Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")}` } }
  );
  const buffer = new Uint8Array(await original.arrayBuffer());

  // resize
  let thumbnail: Uint8Array | null = null;
  ImageMagick.read(buffer, (img) => {
    img.resize(200, 200);
    img.write((data) => { thumbnail = data; });
  });

  // uploader la thumbnail
  await fetch(
    `${Deno.env.get("SUPABASE_URL")}/storage/v1/object/avatars-thumbs/${path}`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")}`,
        "Content-Type": "image/jpeg",
      },
      body: thumbnail,
    }
  );

  return new Response("OK");
});

Brancher le déclencheur : Database → Webhooks → Create a new hook → événement INSERT sur la table storage.objects filtrée sur le bucket avatars. Tu pointes l’URL vers ta fonction. C’est tout.

Les pièges à éviter

J’ai cramé une demi-journée la première fois sur chacun de ces problèmes. Apprends de mes erreurs :

1. Les imports ESM cassent souvent. Préfère esm.sh à cdn.skypack.dev (plus à jour) et précise ?target=denonext pour les libs qui utilisent des features Node spécifiques. Si une lib ne marche pas, va voir si elle est listée sur deno.land/x.

2. Les variables d’environnement ne sont PAS injectées en local automatiquement. Crée un fichier supabase/functions/.env.local avec tes secrets et lance supabase functions serve --env-file ./supabase/functions/.env.local.

3. La taille du payload est limitée à 6 MB. Si tu reçois des fichiers plus gros, fais d’abord un upload sur Storage puis appelle la fonction avec juste le chemin.

4. Les cold starts mangent le timeout. Sur le plan gratuit, si une fonction n’a pas été appelée depuis 1h, le cold start peut prendre 800ms. Pour les webhooks critiques (Stripe par exemple), envisage de “ping” ta fonction toutes les 5 min via un cron pour la garder chaude.

5. Pas de filesystem persistant. Si tu veux écrire un fichier temporaire, utilise Deno.makeTempFile() mais sache que le fichier disparaît à la fin de l’invocation. Pour persister, passe par Storage.

Monitoring et logs

Tu as accès aux logs en temps réel via :

supabase functions logs ma-fonction --tail

Ou directement dans le dashboard Supabase, onglet Logs. Pour du monitoring plus sérieux (alertes en cas d’erreur), branche un Sentry ou Better Stack :

import * as Sentry from "https://deno.land/x/sentry@7.74.0/index.mjs";

Sentry.init({ dsn: Deno.env.get("SENTRY_DSN") });

serve(async (req) => {
  try {
    // ton code
  } catch (err) {
    Sentry.captureException(err);
    throw err;
  }
});

Sur les projets où la fiabilité compte (webhook de paiement par exemple), Sentry me prévient sur Slack en 10 secondes si une fonction crashe. Ça vaut largement les 26 €/mois du plan team.

Coûts réels

Le plan gratuit Supabase inclut :

  • 500 000 invocations / mois
  • 100 GB-secondes de compute / mois
  • Storage Edge Functions illimité

Au-delà, c’est 2 $ par million d’invocations supplémentaires. Pour mettre les choses en perspective : sur un client qui reçoit 800 webhooks Stripe par jour, on est à 24 000 invocations/mois. Largement dans le gratuit. Sur un autre qui ingère 50 events/seconde toute la journée, on monte à 130M/mois, donc environ 260 $. Toujours moins cher qu’un VPS dédié + Redis + monitoring + maintenance.

Quand NE PAS utiliser les Edge Functions

Soyons honnêtes, c’est pas la solution à tout. Évite si :

  • Tu as besoin d’exécutions > 150 secondes (vidéo, ML lourd)
  • Tu veux du WebSocket persistant (utilise Supabase Realtime à la place)
  • Tu as besoin d’un environnement Node spécifique (certaines libs npm ne marchent pas en Deno)
  • Tu veux un cron natif avec UI (passe par n8n ou un workflow visuel)

Pour ces cas-là, je passe sur un VPS avec n8n self-hosté ou un container Railway. Et si le besoin est juste “déclencher un truc tous les jours sans coder”, n8n reste plus rapide.

Conclusion

Supabase Edge Functions, c’est la stack que je recommande à 100% pour automatiser des micro-tâches en 2026 : webhooks, transformations, intégrations API, traitements ponctuels. Tu écris du TypeScript propre, tu versionnes dans Git, tu déploies en une commande, et tu paies zéro tant que tu restes raisonnable.

Si tu hésites entre cette stack et un workflow no-code n8n/Make pour ton projet, j’ai écrit un comparatif détaillé. Et si tu veux qu’on regarde ensemble comment automatiser un process spécifique dans ton entreprise, prends rendez-vous pour un audit d’automatisation gratuit. En 45 minutes, on cartographie tes process et je te dis si Supabase Edge Functions est le bon choix pour toi - ou si autre chose serait plus pertinent.

Partager cet article

Décrivez votre besoin en 2 min, je vous réponds sous 4 h

Audit gratuit · Pas de relance commerciale · Vous repartez avec un plan d'action utilisable.