Bramka AI dla
Supabase Edge Functions.
Zweryfikuj użytkownika Supabase, przekaż jego identyfikator w jednym nagłówku, a każde wywołanie AI zostanie zmierzone, ograniczone i przypisane do rozliczenia tej osoby. Dowolny model, jedno API zgodne z OpenAI, bez instalowania pakietu.
Twoja funkcja ustala, kto pyta. Resztą zajmuje się odnoga.
- Twoja aplikacja
Wywołuje Edge Function z sesją zalogowanego użytkownika Supabase.
Twoja Edge Function
Weryfikuje JWT, odrzuca niezweryfikowanych, dodaje x-odnoga-end-user.
- odnoga
Sprawdza środki użytkownika, kieruje do modelu, zapisuje koszt i czas odpowiedzi dla tej osoby.
Od Edge Function do pierwszego zmierzonego wywołania
- 1
Załóż bezpłatne konto w odnodze i utwórz klucz API.
W obszarze roboczym otwórz „Klucze API” i wybierz „Nowy klucz”. - 2
Zapisz go jako sekret Supabase.
supabase secrets set ODNOGA_API_KEY=sk_live_… - 3
Dodaj helper i wywołuj go z dowolnej funkcji.
Jeden wspólny plik. Każda funkcja, która go importuje, jest weryfikowana, mierzona i śledzona dla każdego użytkownika.
// supabase/functions/_shared/odnoga.ts
import { createClient } from "jsr:@supabase/supabase-js@2";
const BASE = "https://api.odnoga.com/v1";
// Resolve the caller, or refuse. Your odnoga key is a paid credential: if
// this throws before it is used, an unauthenticated request cannot spend it.
export async function requireUser(req: Request): Promise<string> {
const jwt = (req.headers.get("authorization") ?? "").split(" ")[1];
if (!jwt) throw new Response("Unauthorized", { status: 401 });
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_ANON_KEY")!,
);
// VERIFY the token. A decoded-but-unverified subject can be forged, and
// the spend would land on someone else's account.
const { data, error } = await supabase.auth.getUser(jwt);
if (error || !data.user) throw new Response("Unauthorized", { status: 401 });
return data.user.id;
}
// One caller per request, bound to the verified Supabase user.
export function odnoga(endUser: string, fn = "edge") {
return (path: string, body: unknown) =>
fetch(BASE + path, {
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${Deno.env.get("ODNOGA_API_KEY")}`,
"x-odnoga-end-user": endUser, // whose usage this is
"x-odnoga-function": fn, // which of your functions spent it
},
body: JSON.stringify(body),
});
}Co zyskujesz po dodaniu helpera
Użytkownik Supabase to użytkownik końcowy
Weryfikujesz JWT przez supabase.auth.getUser() i wysyłasz identyfikator w jednym nagłówku, x-odnoga-end-user. Od tej chwili każde wywołanie, koszt i limit należą do tej osoby. Identyfikator przypominający e-mail lub numer telefonu jest przed zapisem zamieniany na skrót.
Jeden sekret, tylko po stronie serwera
supabase secrets set ODNOGA_API_KEY=… Klucz jest w środowisku Edge Function — nigdy w przeglądarce ani w repozytorium.
Środki dla każdego użytkownika
Każdy użytkownik Supabase może mieć własny portfel. Gdy środki się skończą, odnoga odpowiada 402 end_user_exhausted, zanim zostanie wywołany jakikolwiek model — nikt nie wyda pieniędzy innej osoby ani Twoich.
Jak to działaRozliczaj użytkowników przez własny Stripe
Plany, płatność i portal rozliczeń dla Twoich użytkowników, powiązane z tym samym identyfikatorem. Przychód trafia na Twoje konto Stripe, nie nasze.
Jak to działaZobacz, która funkcja wydaje
Nazwij wywołującego nagłówkiem x-odnoga-function (i x-odnoga-app), a każda Edge Function dostanie własny koszt, liczbę wywołań i błędy oraz ostrzeżenie, gdy jej ruch ustanie lub gwałtownie wzrośnie.
Jak to działaWywołania z przeglądarki tokenami eut_
Twoja funkcja generuje krótkotrwały token dla jednego użytkownika przez POST /v1/sessions (klucz o zakresie przeglądarki i dozwolony origin; domyślnie godzina, najwyżej 24). Przeglądarka łączy się z odnogą bezpośrednio i nie widzi kosztów ani dostawcy.
Jak to działaRAG z pgvector, dane zostają u Ciebie
Embeddingi i czat przez odnogę, wyszukiwanie wektorowe we własnej bazie, pod Twoim RLS. odnoga nie przechowuje embeddingów i nie łączy się z Twoim projektem.
Streaming, embeddingi, obrazy
Zgodne z OpenAI /chat/completions (ze streamingiem lub bez), /embeddings i /images/generations dla modeli z katalogu, za tym samym helperem.
Każde wywołanie to osobny zapis
Użytkownik, funkcja, model, wersja promptu, tokeny, koszt i czas odpowiedzi dla każdego wywołania — do przeszukania w panelu i do eksportu dla jednej osoby na potrzeby RODO.
Jak to działaTrzy funkcje, które pewnie napiszesz
Wszystkie importują helper powyżej. Nazwy modeli pochodzą z aktualnego katalogu.
import { odnoga, requireUser } from "../_shared/odnoga.ts";
Deno.serve(async (req) => {
let userId: string;
try {
userId = await requireUser(req);
} catch (refusal) {
return refusal as Response; // 401, before the paid key is touched
}
const { message } = await req.json();
const ai = odnoga(userId, "chat");
const r = await ai("/chat/completions", {
model: "gpt-5.1",
messages: [{ role: "user", content: message }],
});
// Pass the status through: a 402 (credits used) or 429 must not
// reach your client as a 200.
return new Response(r.body, {
status: r.status,
headers: { "content-type": "application/json" },
});
});Bezpośrednio do dostawcy czy przez odnogę?
Łącz się bezpośrednio, jeśli…
- — jeden model jednego dostawcy wystarczy Ci na zawsze
- — nie musisz wiedzieć, ile kosztuje każdy użytkownik
- — nikt nie płaci Ci za AI w aplikacji
Dodaj odnogę, gdy…
- — chcesz znać koszt AI każdego użytkownika, a nie jedną sumę miesięczną
- — wydatki użytkownika mają zatrzymać się na limicie, zanim przyjdzie rachunek
- — chcesz zmieniać modele bez ponownego wdrażania funkcji
- — rozliczasz użytkowników za użycie przez Stripe
Czy to oficjalny produkt Supabase?
Nie. odnoga to niezależna bramka AI, niezwiązana z Supabase. Integracja to zwykłe wywołanie HTTPS z Twojej Edge Function, więc nic w Twoim projekcie od nas nie zależy.
Czy muszę instalować pakiet?
Nie. Jeden plik w supabase/functions/_shared i jeden sekret. Endpoint jest zgodny z OpenAI, więc działa też każde SDK OpenAI po ustawieniu adresu bazowego na odnogę i dodaniu nagłówka.
Czy odnoga weryfikuje JWT moich użytkowników?
Nie — robi to Twoja Edge Function przez supabase.auth.getUser(). odnoga przypisuje użycie do identyfikatora, który wysyłasz, i właśnie dlatego helper odrzuca niezweryfikowane wywołanie, zanim użyje klucza.
A co z row-level security?
Bez zmian. odnoga nie łączy się z Twoją bazą. Klient Supabase i RLS nadal decydują, co widzi każdy użytkownik; odnoga zapisuje tylko wysłany identyfikator, by przypisać koszt.
Czy mogę ograniczyć wydatki jednego użytkownika?
Tak, przez portfele użytkowników: gdy środki się skończą, kolejne wywołanie zostanie odrzucone kodem 402. Budżety i alerty kwotowe działają dodatkowo dla całego obszaru roboczego.
Czy przeglądarka może łączyć się z odnogą bezpośrednio?
Tak. Utwórz klucz o zakresie przeglądarki z dozwolonym originem strony, wygeneruj w Edge Function token eut_ dla zalogowanego użytkownika przez POST /v1/sessions i użyj go jako bearer. Klucz serwerowy nie opuszcza serwera.
Moja aplikacja powstała w Lovable. Czy to mnie dotyczy?
Tak — aplikacje Lovable działają na Supabase, także w Lovable Cloud. Na stronie o Lovable jest jedno polecenie, po którym Lovable sam napisze ten helper.
Budujesz w Lovable? Jedno polecenie skonfiguruje to za Ciebie