Edge Functions
Fonctions côté serveur pour les paiements, les e-mails et les endpoints API.
Vue d'ensemble
ScaleRocket utilise les Edge Functions Supabase pour toute la logique côté serveur. Elles s'exécutent sur Deno Deploy, à proximité des utilisateurs dans le monde entier, et gèrent les paiements, les e-mails, la validation de l'authentification et les endpoints API personnalisés.
ScaleRocket utilise les fonctions Convex pour toute la logique côté serveur. Elles s'exécutent sur la plateforme cloud Convex et gèrent les paiements, les e-mails, la validation de l'authentification et les endpoints API personnalisés. Convex fournit trois types de fonctions : queries (lecture), mutations (écriture) et actions (appels API externes).
Que sont-elles
Les Edge Functions sont des fonctions TypeScript qui s'exécutent sur le réseau edge de Supabase (Deno Deploy). Elles remplacent les routes API traditionnelles et les fonctions serverless :
- Runtime : Deno (pas Node.js)
- Emplacement :
supabase/functions/ - Pattern d'URL :
https://<project>.supabase.co/functions/v1/<function-name> - Authentification : Vérification JWT intégrée
Les fonctions Convex sont des fonctions TypeScript qui s'exécutent sur la plateforme Convex. Elles remplacent les routes API traditionnelles et les fonctions serverless :
- Runtime : Node.js (V8 isolate)
- Emplacement :
convex/ - Accès : Hooks typés (
useQuery,useMutation,useAction) - Authentification : Intégrée via
@convex-dev/auth - Endpoints HTTP :
convex/http.tspour les webhooks et appels externes
Fonctions disponibles
| Fonction | Objectif | Déclencheur |
|---|---|---|
stripe-checkout | Crée une session Stripe Checkout | Appel client |
stripe-portal | Crée une session Stripe Billing Portal | Appel client |
stripe-webhook | Traite les événements webhook Stripe | Stripe |
use-credits | Déduit des crédits de manière atomique | Appel client |
delete-account | Supprime le compte utilisateur et ses données | Appel client |
| Fonction | Type | Objectif | Déclencheur |
|---|---|---|---|
stripe.createCheckoutSession | Action | Crée une session Stripe Checkout | Appel client |
stripe.createPortalSession | Action | Crée une session Stripe Billing Portal | Appel client |
stripe.handleWebhook | Endpoint HTTP | Traite les événements webhook Stripe | Stripe |
credits.deductCredits | Mutation | Déduit des crédits de manière atomique | Appel client |
users.deleteAccount | Action | Supprime le compte utilisateur et ses données | Appel client |
Structure des répertoires
supabase/functions/
├── _shared/ # Shared utilities
│ └── supabase.ts # corsHeaders helper and admin client
├── stripe-checkout/
│ └── index.ts
├── stripe-portal/
│ └── index.ts
├── stripe-webhook/
│ └── index.ts
├── use-credits/
│ └── index.ts
└── delete-account/
└── index.tsconvex/
├── _generated/ # Types auto-générés (ne pas modifier)
│ ├── api.d.ts
│ └── server.d.ts
├── schema.ts # Schéma de base de données
├── auth.config.ts # Configuration d'authentification
├── http.ts # Endpoints HTTP (webhooks)
├── stripe.ts # Handlers checkout, portail, webhook Stripe
├── credits.ts # Solde et déduction de crédits
└── users.ts # Gestion des profils et comptes utilisateurUtilitaires partagés
Le répertoire _shared/ contient du code réutilisable importé par toutes les fonctions. Il n'est pas déployé en tant que fonction lui-même. Tout se trouve dans un seul fichier supabase.ts.
_shared/supabase.ts
// supabase/functions/_shared/supabase.ts
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
export const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers":
"authorization, x-client-info, apikey, content-type",
};
export const supabaseAdmin = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
);Chaque fonction commence par une vérification CORS et importe depuis _shared/supabase.ts :
import { corsHeaders, supabaseAdmin } from "../_shared/supabase.ts";
Deno.serve(async (req) => {
// Handle CORS preflight
if (req.method === "OPTIONS") {
return new Response("ok", { headers: corsHeaders });
}
try {
// Verify the user's JWT
const authHeader = req.headers.get("Authorization");
if (!authHeader) throw new Error("No authorization header");
const token = authHeader.replace("Bearer ", "");
const { data: { user }, error } = await supabaseAdmin.auth.getUser(token);
if (error || !user) throw new Error("Invalid token");
// ... function logic
return new Response(JSON.stringify({ success: true }), {
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 401,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
});Les fonctions Convex partagent les utilitaires via des imports TypeScript classiques. L'authentification est gérée via la bibliothèque @convex-dev/auth :
// convex/stripe.ts
import { action, internalMutation } from "./_generated/server";
import { v } from "convex/values";
import { getAuthUserId } from "@convex-dev/auth/server";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export const createCheckoutSession = action({
args: { priceId: v.string() },
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);
if (!userId) throw new Error("Unauthorized");
// ... logique de la fonction
},
});Pas besoin de gestion CORS -- Convex gère cela automatiquement pour les appels de fonctions client.
Créer une nouvelle fonction
1. Générer la fonction
pnpm supabase functions new my-functionCela crée supabase/functions/my-function/index.ts.
2. Écrire le handler
// supabase/functions/my-function/index.ts
import { corsHeaders, supabaseAdmin } from "../_shared/supabase.ts";
Deno.serve(async (req) => {
if (req.method === "OPTIONS") {
return new Response("ok", { headers: corsHeaders });
}
try {
const authHeader = req.headers.get("Authorization");
if (!authHeader) throw new Error("No authorization header");
const token = authHeader.replace("Bearer ", "");
const { data: { user }, error: authError } = await supabaseAdmin.auth.getUser(token);
if (authError || !user) throw new Error("Invalid token");
const { someParam } = await req.json();
// Your logic here
const { data, error } = await supabaseAdmin
.from("my_table")
.select("*")
.eq("user_id", user.id);
return new Response(JSON.stringify({ data }), {
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 400,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
});3. Tester localement
pnpm supabase functions serve my-function4. Appeler depuis le client
const { data, error } = await supabase.functions.invoke("my-function", {
body: { someParam: "value" },
});5. Déployer
pnpm supabase functions deploy my-function1. Créer un fichier dans le répertoire convex/
Créez convex/myFeature.ts (ou ajoutez à un fichier existant).
2. Écrire la fonction
// convex/myFeature.ts
import { query, mutation } from "./_generated/server";
import { v } from "convex/values";
import { getAuthUserId } from "@convex-dev/auth/server";
export const getData = query({
args: {},
handler: async (ctx) => {
const userId = await getAuthUserId(ctx);
if (!userId) throw new Error("Unauthorized");
return await ctx.db
.query("myTable")
.withIndex("by_userId", (q) => q.eq("userId", userId))
.collect();
},
});
export const createItem = mutation({
args: { content: v.string() },
handler: async (ctx, args) => {
const userId = await getAuthUserId(ctx);
if (!userId) throw new Error("Unauthorized");
return await ctx.db.insert("myTable", {
userId,
content: args.content,
});
},
});3. Tester localement
Les fonctions sont disponibles automatiquement en exécutant npx convex dev.
4. Appeler depuis le client
import { useQuery, useMutation } from "convex/react";
import { api } from "../convex/_generated/api";
const data = useQuery(api.myFeature.getData);
const createItem = useMutation(api.myFeature.createItem);
await createItem({ content: "value" });5. Déployer
npx convex deployVariables d'environnement
Les Edge Functions accèdent aux secrets via Deno.env.get(). Définissez-les avec :
# Local development -- add to supabase/.env
STRIPE_SECRET_KEY=sk_test_xxx
# Production
pnpm supabase secrets set STRIPE_SECRET_KEY=sk_live_xxxSUPABASE_URL, SUPABASE_ANON_KEY et SUPABASE_SERVICE_ROLE_KEY sont disponibles automatiquement.
Les fonctions Convex accèdent aux variables d'environnement via process.env. Définissez-les avec :
# Développement local (utilise le déploiement dev)
npx convex env set STRIPE_SECRET_KEY sk_test_xxx
# Production
npx convex env set STRIPE_SECRET_KEY sk_live_xxx --prodListez toutes les variables d'environnement :
npx convex env listFini ? Marquez cette page comme terminée.