Claude for Developers

Claude API with Supabase: Build RAG Apps with Edge Functions

Supercharge your AI apps by integrating Claude API with Supabase's vector search and edge functions for scalable RAG. Build production-ready retrieval systems in under an hour.

A

Andrew Snyder

AI & Automation Editor

December 28, 2025 min read
Share:

Why Claude + Supabase for RAG?

Retrieval-Augmented Generation (RAG) combines vector search with LLMs like Claude to deliver accurate, context-rich responses. Supabase's pgvector extension handles embeddings at scale, while edge functions provide serverless inference. Pair this with Claude's superior reasoning (e.g., Claude 3.5 Sonnet), and you get low-latency, cost-effective RAG apps.

This guide walks you through building a full RAG pipeline:

  • Ingest documents into a vector store
  • Query with semantic search
  • Generate responses via Claude API

Perfect for developers building AI agents, chatbots, or knowledge bases.

Prerequisites

Before diving in:

Pro Tip: Use Claude 3.5 Sonnet (claude-3-5-sonnet-20241022) for best RAG performance—its 200K context window handles large retrieved chunks effortlessly.

Step 1: Create a Supabase Project and Enable pgvector

  1. Log in to Supabase Dashboard and create a new project.
  2. In the SQL Editor, run:
-- Enable pgvector extension
CREATE EXTENSION IF NOT EXISTS vector;

-- Create documents table
CREATE TABLE documents (
  id BIGSERIAL PRIMARY KEY,
  content TEXT NOT NULL,
  metadata JSONB,
  embedding VECTOR(1536) -- OpenAI text-embedding-3-small dimension
);

-- Index for fast vector search
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops);
  1. Set up Row Level Security (RLS) for production:
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Public read" ON documents FOR SELECT USING (true);

This sets up your vector database. pgvector's cosine similarity ensures semantic matches.

Step 2: Ingest Documents with Embeddings

We'll create an edge function to chunk, embed, and store docs. Edge functions run on Deno for global low-latency.

  1. In Supabase Dashboard > Edge Functions, create upsert-docs:
// supabase/functions/upsert-docs/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";

const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};

serve(async (req) => {
  if (req.method === "OPTIONS") {
    return new Response("ok", { headers: corsHeaders });
  }

  const supabase = createClient(
    Deno.env.get("SUPABASE_URL") ?? "",
    Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? ""
  );

  const { text, metadata } = await req.json();
  const openaiApiKey = Deno.env.get("OPENAI_API_KEY")!;

  // Chunk text (simple split for demo)
  const chunks = text.match(/[^.\
]{1,800}/g) || [];

  for (const chunk of chunks) {
    // Embed with OpenAI
    const embedRes = await fetch("https://api.openai.com/v1/embeddings", {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${openaiApiKey}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        model: "text-embedding-3-small",
        input: chunk,
      }),
    });
    const { data: [{ embedding }] } = await embedRes.json();

    // Upsert to Supabase
    await supabase.from("documents").insert({
      content: chunk,
      metadata,
      embedding,
    });
  }

  return new Response(JSON.stringify({ success: true }), {
    headers: { ...corsHeaders, "Content-Type": "application/json" },
  });
});
  1. Deploy: supabase functions deploy upsert-docs
  2. Set env vars in Dashboard: OPENAI_API_KEY
  3. Test: POST to /functions/v1/upsert-docs with { "text": "Your doc content", "metadata": {} }

Step 3: Build the RAG Query Edge Function

Now, the core: query → embed → retrieve → Claude.

Create rag-query function:

// supabase/functions/rag-query/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";

const corsHeaders = { /* same as above */ };

serve(async (req) => {
  if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });

  const supabase = createClient(/* same as above */);
  const { query } = await req.json();
  const openaiApiKey = Deno.env.get("OPENAI_API_KEY")!;
  const anthropicApiKey = Deno.env.get("ANTHROPIC_API_KEY")!;

  // 1. Embed query
  const embedRes = await fetch("https://api.openai.com/v1/embeddings", {
    // same as above, input: query
  });
  const { data: [{ embedding: queryEmbedding }] } = await embedRes.json();

  // 2. Vector search (top 5)
  const { data: docs } = await supabase.rpc("match_documents", {
    query_embedding: queryEmbedding,
    match_threshold: 0.78,
    match_count: 5,
  });

  const context = docs.map(d => d.content).join("\
\
");

  // 3. Call Claude API
  const claudeRes = await fetch("https://api.anthropic.com/v1/messages", {
    method: "POST",
    headers: {
      "x-api-key": anthropicApiKey,
      "anthropic-version": "2023-06-01",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "claude-3-5-sonnet-20241022",
      max_tokens: 1024,
      messages: [{
        role: "user",
        content: `Use this context to answer: ${context}\
\
Question: ${query}`,
      }],
    }),
  });
  const { content: [{ text: response }] } = await claudeRes.json();

  return new Response(JSON.stringify({ response, sources: docs }), {
    headers: { ...corsHeaders, "Content-Type": "application/json" },
  });
});

Key RPC Function: Add this SQL for similarity search:

CREATE OR REPLACE FUNCTION match_documents(
  query_embedding VECTOR(1536),
  match_threshold FLOAT,
  match_count INT
)
RETURNS TABLE (
  id BIGINT, content TEXT, metadata JSONB, similarity FLOAT
)
LANGUAGE SQL STABLE
AS $$
  SELECT
    documents.id,
    documents.content,
    documents.metadata,
    1 - (documents.embedding <=> query_embedding) AS similarity
  FROM documents
  WHERE 1 - (documents.embedding <=> query_embedding) > match_threshold
  ORDER BY documents.embedding <=> query_embedding
  LIMIT match_count;
$$;

Deploy: supabase functions deploy rag-query (add ANTHROPIC_API_KEY env).

Step 4: Test Your RAG Pipeline

  1. Upsert sample docs:
curl -X POST https://your-project.supabase.co/functions/v1/upsert-docs \
  -H "Authorization: Bearer YOUR_SERVICE_ROLE" \
  -H "Content-Type: application/json" \
  -d '{"text": "Claude 3.5 Sonnet excels in coding. It beats GPT-4o on benchmarks.", "metadata": {"source": "anthropic.com"}}'
  1. Query:
curl -X POST https://your-project.supabase.co/functions/v1/rag-query \
  -H "Content-Type: application/json" \
  -d '{"query": "What is Claude good at?"}'

Expect: Response citing your doc, with similarity scores.

Step 5: Add a Simple Frontend

For demo, create an HTML page:

<!DOCTYPE html>
<html>
<head><title>Claude RAG</title></head>
<body>
  <input id="query" placeholder="Ask about your docs...">
  <button onclick="ask()">Query</button>
  <div id="response"></div>

  
</body>
</html>

Host on Vercel/Netlify. Boom—interactive RAG app!

Step 6: Optimize for Scale

  • Chunking: Use recursive splitting for better granularity.
  • Hybrid Search: Combine with full-text search:
    -- Add to match_documents
    AND content ILIKE '%' || query_text || '%'
    
  • Rate Limits: Claude: 50 RPM (Sonnet). Use queues for bursts (e.g., Upstash Redis via Supabase).
  • Costs: Embeddings ~$0.02/1M tokens, Claude ~$3/1M input.

Step 7: Secure Your App

  • Use anon/public keys for client, service_role for functions.
  • RLS policies: Limit inserts to auth users.
  • Env secrets: Never hardcode API keys.

Step 8: Integrate with AI Agents

Extend to agents: Use Claude's tool-use for dynamic retrieval.

Prompt example:

{
  "role": "user",
  "content": [
    {"type": "text", "text": "Answer using tools if needed."},
    {
      "type": "tool",
      "tool_use_id": "toolu_123",
      "name": "rag_search",
      "input": {"query": "{{query}}"}
    }
  ]
}

Call your edge function as a tool.

Step 9: Monitor and Debug

  • Supabase Logs: Dashboard > Edge Functions > Logs.
  • Claude Console: Track usage.
  • Add tracing: OpenTelemetry in Deno.

Step 10: Deploy to Production

  1. Custom domain for edge functions.
  2. Auto-scaling: Supabase handles it.
  3. CI/CD: GitHub Actions with supabase functions deploy.
  4. Multi-region: Edge runtime is global.

Next Level: Integrate with n8n/Zapier for workflows or Claude Code for local dev.

Your RAG app is live! Scale to millions of vectors with Supabase's Postgres. Questions? Drop in comments.

(~1450 words)

The #1 Newsletter in AI

Stay ahead of the AI curve

The most important updates, news, and content — delivered in one weekly newsletter.

No spam. Unsubscribe anytime. Privacy policy

claude api
supabase
rag
edge functions
ai agents
ai-agents
A

About Andrew Snyder

AI & Automation Editor

Andrew covers practical AI automation, workflow design, and the tools teams use to streamline everyday operations.

Comments (0)