IA Engineering

Supabase Edge Functions : fonctions serverless TypeScript sur Deno

Créer, tester et déployer une Edge Function Supabase, sécuriser les appels avec withSupabase, gérer les secrets, recevoir un webhook Stripe et connaître les limites. Code aligné sur la documentation officielle au 30/09/2026.

Edge Functions Supabase serverless Deno CDN global

L’essentiel en 30 secondes

  • Les Edge Functions sont des fonctions TypeScript côté serveur sur un runtime compatible Deno : new, serve pour tester en local, deploy pour publier.
  • Le modèle actuel utilise withSupabase (npm:@supabase/server), qui vérifie l'appelant selon un mode (user, secret, publishable, none) et gère CORS.
  • Les secrets se posent avec supabase secrets set, sans redéploiement ; les noms commençant par SUPABASE_ sont réservés.
  • Limites principales : 256 Mo de mémoire, 2 s de CPU par requête, 150 s (Free) ou 400 s (payant) de durée de worker ; 500 000 appels inclus en Free.

Les Edge Functions de Supabase sont des fonctions TypeScript exécutées côté serveur, sur un runtime compatible Deno distribué au plus près des utilisateurs. Elles servent aux webhooks (Stripe, GitHub), aux e-mails transactionnels, aux appels à des API d'IA et à toute logique qui exige une clé secrète1. Le cycle tient en trois commandes de la CLI : supabase functions new, supabase functions serve pour tester en local, supabase functions deploy pour publier2.

Pour la place des Edge Functions dans l'architecture Supabase, voir notre vue d'ensemble de Supabase.

Comment créer, tester et déployer une fonction ?

Le test local demande Docker (ou un runtime compatible) et une stack locale démarrée2.

supabase init                      # si le projet n'est pas encore initialisé
supabase functions new hello-world # crée supabase/functions/hello-world/index.ts

supabase start                     # stack locale (Docker)
supabase functions serve hello-world
# → http://localhost:54321/functions/v1/hello-world, rechargement à chaud

supabase login
supabase link --project-ref <project_ref>
supabase functions deploy hello-world   # ou sans nom pour tout déployer

Sans Docker, deploy bascule automatiquement sur un empaquetage côté serveur (option --use-api). La fonction est ensuite disponible à https://<project_ref>.supabase.co/functions/v1/hello-world2. Pour le reste de la CLI, voir notre guide du développement local avec la CLI.

À quoi ressemble une fonction aujourd'hui ?

Le modèle généré par la CLI utilise désormais withSupabase, du paquet @supabase/server. Ce wrapper vérifie les identifiants de l'appelant selon un mode déclaré, gère CORS et les requêtes préliminaires OPTIONS, et fournit des clients Supabase prêts à l'emploi34 :

// supabase/functions/mes-commandes/index.ts
import { withSupabase } from 'npm:@supabase/server'

export default {
  fetch: withSupabase({ auth: 'user' }, async (_req, ctx) => {
    // ctx.supabase      : client limité aux droits RLS de l'utilisateur
    // ctx.supabaseAdmin : client qui contourne la RLS
    // ctx.userClaims    : identité issue du JWT (id, email, rôle)
    const { data, error } = await ctx.supabase
      .from('orders')
      .select('id, total, created_at')
      .order('created_at', { ascending: false })

    if (error) return Response.json({ error: error.message }, { status: 400 })
    return Response.json(data)
  }),
}
Mode authAccepteUsage
'user'Un JWT utilisateur valide dans AuthorizationAppels depuis l'application, verify_jwt laissé à true
'secret'Une clé secret dans apikeyCron, workers, pg_net, autre fonction
'publishable'Une clé publishable dans apikeyAppels publics identifiés par l'application
'none'Tout appelantWebhooks signés, à vérifier dans la fonction

Plusieurs modes peuvent être combinés (auth: ['user', 'secret']) ; ctx.authMode indique lequel a correspondu3.

Correction : l'ancienne version de cet article importait serve depuis https://deno.land/std@0.168.0/http/server.ts et supabase-js depuis esm.sh. La documentation actuelle utilise export default { fetch } ou Deno.serve, et les imports npm:45. Si vous n'utilisez pas withSupabase, importez les en-têtes CORS depuis npm:@supabase/supabase-js@^2/cors (supabase-js 2.95.0 et plus) plutôt que de les recopier à la main4.

Quelles variables d'environnement sont disponibles ?

Chaque fonction reçoit d'office5 :

  • SUPABASE_URL et SUPABASE_DB_URL (connexion directe à Postgres) ;
  • SUPABASE_PUBLISHABLE_KEYS et SUPABASE_SECRET_KEYS, deux dictionnaires JSON de clés nommées ;
  • SUPABASE_JWKS, pour vérifier les JWT des utilisateurs ;
  • les anciennes SUPABASE_ANON_KEY et SUPABASE_SERVICE_ROLE_KEY, qui correspondent à des clés dépréciées d'ici fin 20266.
import { createClient } from 'npm:@supabase/supabase-js@2'

const secretKeys = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)
const supabaseAdmin = createClient(Deno.env.get('SUPABASE_URL')!, secretKeys['default'])

Comment gérer les secrets ?

En local, les fonctions lisent supabase/functions/.env (à ajouter au .gitignore). En production5 :

supabase secrets set STRIPE_SECRET_KEY=sk_live_...
supabase secrets set --env-file .env     # tout un fichier
supabase secrets list

Les secrets sont disponibles immédiatement, sans redéploiement. Un nom de secret ne peut pas commencer par SUPABASE_, préfixe réservé ; un projet accepte 100 secrets au maximum57. Créer ou supprimer un secret de production exige le rôle Owner ou Administrator5.

Comment recevoir un webhook Stripe ?

Un fournisseur externe n'envoie pas d'identifiants Supabase : on désactive verify_jwt pour cette fonction, on utilise auth: 'none' et on vérifie la signature du fournisseur dans le code. Sous Deno, la vérification Stripe doit passer par la version asynchrone et le SubtleCryptoProvider3.

# supabase/config.toml
[functions.stripe-webhook]
verify_jwt = false
import { withSupabase } from 'npm:@supabase/server'
import Stripe from 'npm:stripe'

const stripe = new Stripe(Deno.env.get('STRIPE_SECRET_KEY')!)
const cryptoProvider = Stripe.createSubtleCryptoProvider()

export default {
  fetch: withSupabase({ auth: 'none' }, async (req, ctx) => {
    const signature = req.headers.get('stripe-signature') ?? ''
    const body = await req.text()
    let event: Stripe.Event
    try {
      event = await stripe.webhooks.constructEventAsync(
        body, signature, Deno.env.get('STRIPE_WEBHOOK_SECRET')!, undefined, cryptoProvider
      )
    } catch (err) {
      console.error('Signature Stripe invalide :', err)
      return new Response('bad signature', { status: 400 })
    }

    if (event.type === 'checkout.session.completed') {
      // ctx.supabaseAdmin : mise à jour de la commande, contourne la RLS
    }
    return Response.json({ received: true })
  }),
}

auth: 'none' désactive toute vérification : la fonction est entièrement responsable d'authentifier l'appelant, ici par la signature3.

Comment appeler une fonction depuis le client ?

// Avec supabase-js : le JWT de l'utilisateur connecté est transmis automatiquement
const { data, error } = await supabase.functions.invoke('mes-commandes', {
  body: { limit: 10 },
})

// Avec fetch
const res = await fetch('https://<project_ref>.supabase.co/functions/v1/mes-commandes', {
  method: 'POST',
  headers: {
    apikey: 'sb_publishable_...',
    Authorization: `Bearer ${session.access_token}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ limit: 10 }),
})

Les clés publishable et secret se passent dans l'en-tête apikey, pas en Authorization: Bearer : ce ne sont pas des JWT6.

Quelles limites et quel coût ?

LimiteValeur
Mémoire256 Mo
Durée de vie d'un worker (horloge)150 s en Free, 400 s sur les plans payants
Temps CPU par requête2 s (hors attente d'entrées-sorties)
Délai sans réponse150 s, puis erreur 504
Taille d'une fonction20 Mo empaquetée par la CLI, 5 Mo côté serveur
Nombre de fonctions100 (Free), 1 000 (Pro), 2 000 (Team)
RéseauPorts sortants 25 et 587 bloqués

Autres restrictions : pas de Web Workers, pas de bibliothèques Node multithread (sharp, libvips), et le HTML n'est servi tel quel qu'avec un domaine personnalisé7. Côté prix, 500 000 appels par mois sont inclus en Free, 2 millions en Pro et Team, puis 2 $ par million8. Pour les traitements longs, Supabase oriente vers les tâches en arrière-plan1.

Pour concevoir une intégration (paiement, IA, CRM) sur Edge Functions, voir notre offre de développement d'intégrations sur mesure ; pour sécuriser les données que ces fonctions manipulent, la sécurité des données avec la RLS.

Sources

  1. Edge Functions (documentation Supabase)
  2. Getting Started with Edge Functions (documentation Supabase)
  3. Securing Edge Functions (documentation Supabase)
  4. CORS support for invoking from the browser (documentation Supabase)
  5. Environment variables (documentation Supabase)
  6. API keys (documentation Supabase)
  7. Edge Functions Limits (documentation Supabase)
  8. Edge Functions Pricing (documentation Supabase)

Écrit par

CTO & Chief Digital Strategist chez AdSim, Liège

Georges est CTO et Chief Digital Strategist d’AdSim.

  • Campaign Manager Brand Controls Basics
  • Bid Manager Brand Controls Basics
  • AdWords Video Brand Controls Basics

Questions fréquentes

Vos questions sur les Edge Functions Supabase

Peut-on écrire une Edge Function en JavaScript ou en Python ?

Supabase ne prend en charge que TypeScript sur le runtime Deno pour créer des Edge Functions. Le JavaScript valide reste du TypeScript valide ; Python n'est pas proposé.

Faut-il redéployer une fonction après avoir changé un secret ?

Non. Les secrets définis avec supabase secrets set ou dans le tableau de bord sont disponibles immédiatement pour les fonctions déployées.

Une Edge Function peut-elle se connecter directement à Postgres ?

Oui, la variable SUPABASE_DB_URL est fournie par défaut. Dans un contexte serverless, préférez un pool ou un pilote adapté, par exemple via le pooler en mode transaction, pour ne pas saturer les connexions.

Commentaires

Chaque commentaire est relu avant publication, en général sous 24 h ouvrées. Les liens promotionnels ne sont pas publiés.

Aucun commentaire pour l’instant. Une question sur l’article ? Posez-la ci-dessous.

Laisser un commentaire

Jamais publié. Sert à vous prévenir d’une réponse.

Votre commentaire sera publié après relecture. Un lien au plus, pas de message promotionnel.

Point de départ

On applique cette méthode à votre compte ?

L’audit gratuit part de vos données, pas d’un exemple. Vous recevez le diagnostic sous 48 h ouvrées.

« Chez AdSim, c’est un vrai expert du digital qui lit votre demande et vous répond sous 48 h ouvrées. »

Valérie Matrige, CEO & co-fondatrice

Réponse sous 48 h ouvrées · Diagnostic 100 % gratuit · Sans engagement · Zéro revente de vos données