Waarom Edge Functions voor AI-integraties
Drie redenen waarom Edge Functions de juiste keuze zijn voor AI API-aanroepen:
1. Beveiliging: je OpenAI/Anthropic API-sleutel bestaat als een server-side secret, nooit blootgesteld aan de client. Niemand die je frontendcode heeft, kan de sleutel extraheren.
2. Databasetoegang: Edge Functions draaien binnen de infrastructuur van Supabase en hebben directe toegang met lage latency tot je PostgreSQL-database. Je kunt gebruikerscontext ophalen, resultaten opslaan en gebruik loggen in dezelfde functie die de AI aanroept.
3. Streamingondersteuning: Edge Functions ondersteunen Response-streaming, waardoor je AI-output woord voor woord naar de client kunt sturen, wat de ervaren snelheid voor lange AI-responses drastisch verbetert.
Een eenvoudige OpenAI-proxyfunctie
De eenvoudigste Edge Function: ontvang een prompt, roep OpenAI aan, retourneer de response.
```typescript import OpenAI from "npm:openai"
const openai = new OpenAI({ apiKey: Deno.env.get("OPENAI_API_KEY") })
Deno.serve(async (req) => {
const { prompt } = await req.json()
const chat = await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: prompt }],
max_tokens: 500
})
return new Response(
JSON.stringify({ result: chat.choices[0].message.content }),
{ headers: { "Content-Type": "application/json" } }
)
})
```
Deploy: supabase functions deploy openai-proxy. Secrets: supabase secrets set OPENAI_API_KEY=sk-....
Authenticatie en rate limiting toevoegen
Elke AI Edge Function moet verifiëren dat de gebruiker is geauthenticeerd en diens gebruikslimieten controleren:
```typescript
import { createClient } from "npm:@supabase/supabase-js"
Deno.serve(async (req) => {
// Verifieer authenticatietoken
const token = req.headers.get("Authorization")?.replace("Bearer ", "")
const supabase = createClient(Deno.env.get("SUPABASE_URL"), Deno.env.get("SUPABASE_SERVICE_ROLE_KEY"))
const { data: { user }, error } = await supabase.auth.getUser(token)
if (error || !user) return new Response("Unauthorized", { status: 401 })
// Controleer rate limit (max 20 verzoeken/uur)
const oneHourAgo = new Date(Date.now() - 3600000).toISOString()
const { count } = await supabase
.from("ai_usage_log")
.select("*", { count: "exact" })
.eq("user_id", user.id)
.gte("created_at", oneHourAgo)
if (count >= 20) return new Response("Rate limit exceeded", { status: 429 })
// ... roep OpenAI aan en log gebruik
})
```
Streaming responses naar WeWeb
Streaming stuurt AI-output geleidelijk naar de client, gebruikers zien tekst woord voor woord verschijnen in plaats van te wachten op de volledige response.
In de Edge Function:
```typescript
const stream = await openai.chat.completions.create({
model: "gpt-4o",
messages,
stream: true
})
const readable = new ReadableStream({ async start(controller) { for await (const chunk of stream) { const text = chunk.choices[0]?.delta?.content || "" controller.enqueue(new TextEncoder().encode(text)) } controller.close() } })
return new Response(readable, {
headers: { "Content-Type": "text/event-stream" }
})
```
In WeWeb: gebruik een custom JavaScript-actie om de stream-URL op te halen en een paginavariabele karakter voor karakter bij te werken naarmate chunks binnenkomen.
Een RAG-pipeline bouwen (Retrieval Augmented Generation)
RAG verbetert AI-antwoorden door relevante kennis in de prompt te injecteren op het moment van de vraag. Architectuur:
1. Kennis-ingestie (eenmalig uitgevoerd): roep voor elk document in je kennisbank de embedding-API van OpenAI aan om een 1536-dimensionale vector te krijgen. Sla vectoren op in Supabase met de pgvector-extensie.
2. Op het moment van de vraag: wanneer een gebruiker een vraag stelt, embed je de vraag (dezelfde embedding-API), en voer je vervolgens een gelijkenis-zoekopdracht uit in Supabase: SELECT content, 1 - (embedding <=> query_embedding) AS similarity FROM documents ORDER BY similarity DESC LIMIT 3.
3. Aangevulde prompt: injecteer de top 3 bijpassende documenten in de systeemprompt: "Beantwoord alleen met de volgende context: [docs]. Als het antwoord niet in de context staat, zeg dan dat je het niet weet."
Resultaat: de AI antwoordt uitsluitend op basis van je documentatie, zonder hallucinatie over zaken die je niet hebt gedocumenteerd.
Cold-start-optimalisatie voor Edge Functions
Supabase Edge Functions zijn Deno-gebaseerd en draaien op het wereldwijde edge-netwerk van Deno Deploy. Een cold start, de eerste aanroep van een functie die recent niet is aangeroepen, duurt doorgaans 200-500ms. Voor AI-functies waarbij gebruikers directe feedback verwachten, kan deze cold-startlatency merkbaar zijn.
Verschillende optimalisatiestrategieën verminderen de impact van cold starts. Importeer ten eerste alleen wat je nodig hebt. Een functie die de volledige OpenAI SDK importeert, voegt meer bundelgewicht toe dan een functie die alleen het ChatCompletion-type importeert. Gebruik named imports en tree-shaking-vriendelijke patronen. Ten tweede, warm kritieke functies vooraf op door ze volgens een schema aan te roepen. Een Supabase cron-job die je AI-functie elke 5 minuten aanroept met een synthetisch verzoek, houdt de instance warm tegen de kosten van een paar API-aanroepen per dag.
Gebruik ten derde response-caching voor deterministische prompts. Stellen gebruikers vaak hetzelfde type vraag (documentsamenvatting, categorieclassificatie), cache dan de output in een Supabase-tabel op basis van een hash van de input. Geef het gecachte resultaat direct terug bij herhaalde inputs, geen cold start, geen API-kosten, response onder 10ms. Dit is bijzonder effectief voor classificatietaken waarbij de set mogelijke inputs begrensd is.
AI-inferentie op de edge: OpenAI en Anthropic aanroepen vanuit Edge Functions
Zowel de OpenAI SDK als de Anthropic SDK werken in de Deno-runtimeomgeving, die Supabase Edge Functions gebruiken. Je importeert ze via npm:-specifiers: import OpenAI from 'npm:openai' en import Anthropic from 'npm:@anthropic-ai/sdk'. Beide SDK's handelen de HTTPS-aanroepen, retrylogica en foutafhandeling voor hun respectievelijke API's af.
Voor een productie-AI-functie kun je de modelkeuze het beste dynamisch afhandelen. Sla de modelnaam op als een Supabase Edge Function-secret in plaats van deze te hardcoden. Zo kun je overschakelen van gpt-4o naar gpt-4o-mini (voor goedkopere taken) of van claude-3-5-sonnet naar claude-3-haiku zonder de functie opnieuw te deployen. Je werkt het secret bij en het volgende verzoek gebruikt het nieuwe model.
Kostenbeheer is cruciaal voor AI-functies met open gebruikerstoegang. Log elke API-aanroep met het tokenaantal dat in de response wordt teruggegeven. Stel maandelijkse uitgavenwaarschuwingen in op je OpenAI- of Anthropic-dashboard. Beperk voor free-tier-gebruikers het gebruik tot een tokenbudget per maand en handhaaf dit in de Edge Function vóórdat de API-aanroep wordt gedaan. Wij bouwen deze kostenbewakingslaag in elke AI-functie die we opleveren, het heeft op meer dan één klantproject onverwachte maandelijkse rekeningen van $3.000 voorkomen.
Streaming responses: het Server-Sent Events-patroon
Het Server-Sent Events (SSE)-patroon is de standaardmanier om AI-responses te streamen van een Supabase Edge Function naar een browser. De functie stelt Content-Type: text/event-stream in en schrijft chunks in het formaat data: {text}\n\n zodra ze binnenkomen van de AI API. De browser gebruikt de native EventSource-API of een fetch met ReadableStream om de stream incrementeel te verwerken.
In WeWeb vereist het implementeren van SSE een custom JavaScript-actie, omdat de ingebouwde HTTP-request-acties wachten op de volledige response voordat ze verdergaan. De actie opent een fetch-verzoek, leest de responsbody als een stream met response.body.getReader(), decodeert elke chunk, en voegt deze toe aan een paginavariabele. Deze paginavariabele is gebonden aan een tekstelement op het canvas, zodat gebruikers de tekst karakter voor karakter zien verschijnen.
Het resultaat is een drastisch betere UX voor de AI-functie. Een AI-response van GPT-4o van 300 woorden duurt ongeveer 5 seconden om te voltooien. Zonder streaming ziet de gebruiker 5 seconden een spinner en verschijnt dan pas de volledige tekst. Met streaming beginnen ze binnen 300ms na het versturen van hun prompt al te lezen. In gebruikerstests scoort streaming consistent als responsiever en intelligenter aanvoelend, ook al is de totale generatietijd hetzelfde.
Edge Functions gebruiken als webhook-handlers
Edge Functions zijn een uitstekende keuze voor het afhandelen van webhooks van externe diensten, Stripe-betalingsevents, GitHub-pushmeldingen, Twilio-sms, of elke dienst die data POST't naar een URL. Ze zijn altijd beschikbaar (geen server die moet opstarten), wereldwijd verspreid (lage latency vanaf het datacenter van de webhook-verzender), en hebben directe Supabase-databasetoegang om records bij te werken naar aanleiding van events.
Een Stripe-webhook Edge Function valideert de webhooksignatuur, parseert het event-type en werkt het relevante Supabase-record bij. Voor een abonnementsupgrade-event: verifieer de signatuur met stripe.webhooks.constructEvent() met je webhooksecret, extraheer het klant-ID en het nieuwe plan, werk de subscriptions-tabel in Supabase bij, en retourneer een 200-response binnen 5 seconden (Stripe's timeout). Dit is de volledige betalingslevenscyclus-handler, geen aparte server nodig.
Voor AI-toepassingen worden webhooks gebruikt voor asynchrone verwerking. Wanneer een gebruiker een document uploadt voor AI-analyse, laat je hem niet wachten, je zet de taak in een wachtrij via een Supabase row-insert, een achtergrondtaak pakt deze op en roept de AI API aan, en zodra het resultaat klaar is, stuurt een Supabase-trigger een pushmelding of werkt het dashboard bij via realtime. Edge Functions handelen zowel de intake-webhook als de uitgaande melding af.
Beveiligingspatronen voor AI Edge Functions
De belangrijkste beveiligingsmaatregel voor elke AI Edge Function is het verifiëren van de identiteit van de aanroeper vóórdat er een API-aanroep wordt gedaan. Extraheer altijd de JWT uit de Authorization: Bearer-header, roep supabase.auth.getUser(token) aan om deze te verifiëren, en controleer of de gebruiker de juiste rol of het juiste abonnementsniveau heeft voor de gevraagde bewerking. Een niet-geauthenticeerde AI-functie is een directe route naar onbeperkte API-uitgaven door iedereen die je endpoint-URL ontdekt.
Secretsbeheer in Supabase Edge Functions gebruikt het CLI-commando supabase secrets set en Deno.env.get() tijdens runtime. Hardcode nooit API-sleutels in de broncode van je functie, zelfs niet in een privé-repository, secrets horen thuis in de secrets store, niet in versiebeheer. Roteer secrets onmiddellijk als een repository per ongeluk publiek wordt gemaakt of als een sleutel in foutlogs terechtkomt.
Inputvalidatie voorkomt prompt-injectie-aanvallen, waarbij een kwaadwillende gebruiker een input samenstelt die het gedrag van de AI verandert. Valideer de inputlengte, strip gevaarlijke tekens, en als de functie een systeemprompt gebruikt die door de gebruiker aangeleverde data bevat, saniteer dan de gebruikersinput vóór interpolatie. Een simpele lengtecontrole (if (prompt.length > 2000) return error) elimineert een categorie misbruik waarbij gebruikers extreem lange prompts samenstellen om de rekenkosten te maximaliseren.