The AI gateway built into your Edge Functions.
No odnoga package to install. Verify the Supabase JWT and forward its subject as one header and every request is metered, billed and traced per end user — without writing the metering layer yourself.
const BASE = "https://api.odnoga.com/functions/v1/airouter-openai-compat/v1";
import { createClient } from "jsr:@supabase/supabase-js@2";
export async function odnoga(req: Request) {
const jwt = (req.headers.get("authorization") ?? "").split(" ")[1];
// Verify the token — a decoded-but-unverified subject can be forged, and the
// spend would land on someone else's account.
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_ANON_KEY")!,
);
const endUser = jwt ? (await supabase.auth.getUser(jwt)).data.user?.id : undefined;
return (path: string, body: unknown) =>
fetch(BASE + path, {
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${Deno.env.get("ODNOGA_API_KEY")}`,
...(endUser ? { "x-odnoga-end-user": endUser } : {}),
},
body: JSON.stringify(body),
});
}
// supabase/functions/chat/index.ts
Deno.serve(async (req) => {
const ai = await odnoga(req);
const r = await ai("/chat/completions", {
model: "auto",
messages: [{ role: "user", content: "Hi" }],
});
return new Response(r.body, { headers: { "content-type": "application/json" } });
});
Set one secret: supabase secrets set ODNOGA_API_KEY=sk_live_xxx
Built for the Supabase stack
Copy-paste helper
One small file in _shared/. No package, no build step, no version to keep up with.
end_user from JWT
Decode auth.uid() from the caller's Authorization header and send it as x-odnoga-end-user. Every request is metered per user.
RAG over pgvector
Embeddings + chat in two calls; supabase.rpc("match_documents") in between. No glue code.
Edge function traceability
Every call is a row: tenant, end-user, prompt version, model, cost, latency.
One secret to set
supabase secrets set ODNOGA_API_KEY=… and you're live.
Streaming, images, embeddings
Same OpenAI-compatible endpoints for every modality — /chat/completions, /embeddings, /images/generations.
RAG over pgvector, end to end
const ai = await odnoga(req);
const { data: emb } = await ai("/embeddings", {
model: "text-embedding-3-small",
input: question,
}).then((r) => r.json());
const { data: docs } = await supabase.rpc("match_documents", {
query_embedding: emb[0].embedding,
match_count: 5,
});
const r = await ai("/chat/completions", {
model: "gpt-4o-mini",
messages: [
{ role: "system", content: `Use:\n${docs.map(d => d.content).join("\n---\n")}` },
{ role: "user", content: question },
],
});Frequently asked
Is this an official Supabase project?
No — we're an independent gateway. The integration is a plain HTTP call from your Edge Function, so nothing in your project depends on us shipping a package.
Is there an npm or JSR package?
A published SDK is in progress. Today the integration is deliberately package-free: an OpenAI-compatible HTTP endpoint plus one header. Any OpenAI SDK works by pointing base_url at odnoga.
What about row-level security?
odnoga never touches your tables. It records the end-user id you send to attribute cost; your Supabase client + RLS continue to govern what data the user sees.
Can I use it from the browser (eut tokens)?
Yes. Mint a short-lived eut_ token from your edge function via POST /v1/sessions, pass it to the browser, and use it as the bearer.