Menu
Nazad na Blog
1 min read
Backend

Supabase: Die Open-Source Firebase Alternative für 2026

Supabase als vollständige Backend-Plattform. PostgreSQL, Realtime, Auth, Edge Functions und Vector Embeddings in einem Stack.

SupabasePostgreSQLRealtime DatabaseEdge FunctionsAuthenticationFirebase Alternative
Supabase: Die Open-Source Firebase Alternative für 2026

Supabase: Die Open-Source Firebase Alternative für 2026

Meta-Description: Supabase als vollständige Backend-Plattform. PostgreSQL, Realtime, Auth, Edge Functions und Vector Embeddings in einem Stack.

Keywords: Supabase, PostgreSQL, Realtime Database, Edge Functions, Authentication, Firebase Alternative, Vector Embeddings


Einführung

Supabase ist die Open-Source Firebase Alternative, die auf PostgreSQL aufbaut. 2026 bietet es eine vollständige Backend-Plattform: Database, Auth, Realtime, Edge Functions, Storage und Vector Embeddings – alles integriert.


Supabase Stack

┌─────────────────────────────────────────────────────────────┐
│                    SUPABASE PLATFORM                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Database Layer:                                            │
│  ├── PostgreSQL (Enterprise-Grade)                         │
│  ├── Row Level Security (RLS)                              │
│  ├── PostgREST (Auto-Generated APIs)                       │
│  └── pgvector (Vector Embeddings)                          │
│                                                             │
│  Realtime Layer:                                            │
│  ├── Postgres Changes (CDC)                                │
│  ├── Broadcast (User-to-User)                              │
│  ├── Presence (Online Status)                              │
│  └── WebSocket Connections                                  │
│                                                             │
│  Auth Layer:                                                │
│  ├── Email/Password                                        │
│  ├── OAuth Providers (Google, GitHub, etc.)                │
│  ├── Magic Links                                           │
│  └── Phone/SMS Auth                                        │
│                                                             │
│  Edge Functions:                                            │
│  ├── Deno Runtime                                          │
│  ├── TypeScript/JavaScript                                 │
│  └── Global Distribution                                   │
│                                                             │
│  Storage:                                                   │
│  ├── S3-Compatible                                         │
│  ├── CDN Integration                                       │
│  └── Image Transformations                                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Database Setup

// lib/supabase.ts
import { createClient } from '@supabase/supabase-js';
import { Database } from './database.types';

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;

export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey);

// Server-Side mit Service Role
import { createClient } from '@supabase/supabase-js';

export const supabaseAdmin = createClient<Database>(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!,
  {
    auth: {
      autoRefreshToken: false,
      persistSession: false
    }
  }
);

Schema Definition (SQL)

-- Users Tabelle (erweitert auth.users)
CREATE TABLE public.profiles (
  id UUID REFERENCES auth.users(id) ON DELETE CASCADE PRIMARY KEY,
  username TEXT UNIQUE,
  full_name TEXT,
  avatar_url TEXT,
  bio TEXT,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Posts Tabelle
CREATE TABLE public.posts (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  author_id UUID REFERENCES public.profiles(id) ON DELETE CASCADE NOT NULL,
  title TEXT NOT NULL,
  content TEXT,
  published BOOLEAN DEFAULT FALSE,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Trigger für updated_at
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER profiles_updated_at
  BEFORE UPDATE ON public.profiles
  FOR EACH ROW EXECUTE FUNCTION update_updated_at();

CREATE TRIGGER posts_updated_at
  BEFORE UPDATE ON public.posts
  FOR EACH ROW EXECUTE FUNCTION update_updated_at();

Row Level Security (RLS)

-- RLS aktivieren
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.posts ENABLE ROW LEVEL SECURITY;

-- Profiles: Jeder kann lesen, nur Owner kann schreiben
CREATE POLICY "Profiles sind öffentlich lesbar"
  ON public.profiles FOR SELECT
  USING (true);

CREATE POLICY "User kann eigenes Profil bearbeiten"
  ON public.profiles FOR UPDATE
  USING (auth.uid() = id);

-- Posts: Veröffentlichte sind lesbar, Autor kann alles
CREATE POLICY "Veröffentlichte Posts sind lesbar"
  ON public.posts FOR SELECT
  USING (published = true OR auth.uid() = author_id);

CREATE POLICY "Autor kann eigene Posts erstellen"
  ON public.posts FOR INSERT
  WITH CHECK (auth.uid() = author_id);

CREATE POLICY "Autor kann eigene Posts bearbeiten"
  ON public.posts FOR UPDATE
  USING (auth.uid() = author_id);

CREATE POLICY "Autor kann eigene Posts löschen"
  ON public.posts FOR DELETE
  USING (auth.uid() = author_id);

CRUD Operations

// CREATE
async function createPost(post: { title: string; content: string }) {
  const { data, error } = await supabase
    .from('posts')
    .insert({
      title: post.title,
      content: post.content,
      author_id: (await supabase.auth.getUser()).data.user?.id
    })
    .select()
    .single();

  if (error) throw error;
  return data;
}

// READ
async function getPosts() {
  const { data, error } = await supabase
    .from('posts')
    .select(`
      *,
      author:profiles(username, avatar_url)
    `)
    .eq('published', true)
    .order('created_at', { ascending: false })
    .limit(10);

  if (error) throw error;
  return data;
}

// READ mit Pagination
async function getPostsPaginated(page: number, pageSize: number = 10) {
  const from = page * pageSize;
  const to = from + pageSize - 1;

  const { data, error, count } = await supabase
    .from('posts')
    .select('*, author:profiles(username)', { count: 'exact' })
    .eq('published', true)
    .range(from, to);

  return { data, count, hasMore: count ? to < count - 1 : false };
}

// UPDATE
async function updatePost(id: string, updates: Partial<Post>) {
  const { data, error } = await supabase
    .from('posts')
    .update(updates)
    .eq('id', id)
    .select()
    .single();

  if (error) throw error;
  return data;
}

// DELETE
async function deletePost(id: string) {
  const { error } = await supabase
    .from('posts')
    .delete()
    .eq('id', id);

  if (error) throw error;
}

Realtime Subscriptions

import { useEffect, useState } from 'react';
import { supabase } from '@/lib/supabase';
import { RealtimeChannel } from '@supabase/supabase-js';

// 1. Database Changes (CDC)
function useRealtimePosts() {
  const [posts, setPosts] = useState<Post[]>([]);

  useEffect(() => {
    // Initial Load
    supabase.from('posts').select('*').then(({ data }) => {
      if (data) setPosts(data);
    });

    // Subscribe to Changes
    const channel = supabase
      .channel('posts-changes')
      .on(
        'postgres_changes',
        {
          event: '*',
          schema: 'public',
          table: 'posts'
        },
        (payload) => {
          if (payload.eventType === 'INSERT') {
            setPosts(prev => [payload.new as Post, ...prev]);
          } else if (payload.eventType === 'UPDATE') {
            setPosts(prev =>
              prev.map(p => p.id === payload.new.id ? payload.new as Post : p)
            );
          } else if (payload.eventType === 'DELETE') {
            setPosts(prev => prev.filter(p => p.id !== payload.old.id));
          }
        }
      )
      .subscribe();

    return () => {
      supabase.removeChannel(channel);
    };
  }, []);

  return posts;
}

// 2. Broadcast (User-to-User Messaging)
function useBroadcast(roomId: string) {
  const [messages, setMessages] = useState<Message[]>([]);

  useEffect(() => {
    const channel = supabase.channel(`room:${roomId}`)
      .on('broadcast', { event: 'message' }, ({ payload }) => {
        setMessages(prev => [...prev, payload]);
      })
      .subscribe();

    return () => {
      supabase.removeChannel(channel);
    };
  }, [roomId]);

  const sendMessage = (content: string) => {
    supabase.channel(`room:${roomId}`).send({
      type: 'broadcast',
      event: 'message',
      payload: { content, timestamp: new Date().toISOString() }
    });
  };

  return { messages, sendMessage };
}

// 3. Presence (Online Status)
function usePresence(roomId: string, userId: string) {
  const [onlineUsers, setOnlineUsers] = useState<string[]>([]);

  useEffect(() => {
    const channel = supabase.channel(`presence:${roomId}`)
      .on('presence', { event: 'sync' }, () => {
        const state = channel.presenceState();
        const users = Object.values(state).flat().map(p => p.user_id);
        setOnlineUsers(users);
      })
      .subscribe(async (status) => {
        if (status === 'SUBSCRIBED') {
          await channel.track({ user_id: userId });
        }
      });

    return () => {
      supabase.removeChannel(channel);
    };
  }, [roomId, userId]);

  return onlineUsers;
}

Authentication

// Sign Up
async function signUp(email: string, password: string) {
  const { data, error } = await supabase.auth.signUp({
    email,
    password,
    options: {
      emailRedirectTo: `${window.location.origin}/auth/callback`
    }
  });
  return { data, error };
}

// Sign In
async function signIn(email: string, password: string) {
  const { data, error } = await supabase.auth.signInWithPassword({
    email,
    password
  });
  return { data, error };
}

// OAuth Sign In
async function signInWithOAuth(provider: 'google' | 'github') {
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider,
    options: {
      redirectTo: `${window.location.origin}/auth/callback`
    }
  });
  return { data, error };
}

// Sign Out
async function signOut() {
  const { error } = await supabase.auth.signOut();
  return { error };
}

// Auth State Hook
function useAuth() {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    supabase.auth.getSession().then(({ data: { session } }) => {
      setUser(session?.user ?? null);
      setLoading(false);
    });

    const { data: { subscription } } = supabase.auth.onAuthStateChange(
      (_event, session) => {
        setUser(session?.user ?? null);
      }
    );

    return () => subscription.unsubscribe();
  }, []);

  return { user, loading };
}

Edge Functions

// supabase/functions/send-email/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 });
  }

  try {
    const supabase = createClient(
      Deno.env.get('SUPABASE_URL')!,
      Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
    );

    const { to, subject, body } = await req.json();

    // Send email via Resend, SendGrid, etc.
    const response = await fetch('https://api.resend.com/emails', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${Deno.env.get('RESEND_API_KEY')}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        from: 'noreply@example.com',
        to,
        subject,
        html: body
      })
    });

    const result = await response.json();

    return new Response(JSON.stringify(result), {
      headers: { ...corsHeaders, 'Content-Type': 'application/json' }
    });
  } catch (error) {
    return new Response(JSON.stringify({ error: error.message }), {
      status: 500,
      headers: { ...corsHeaders, 'Content-Type': 'application/json' }
    });
  }
});

// Client-Side Aufruf
const { data, error } = await supabase.functions.invoke('send-email', {
  body: {
    to: 'user@example.com',
    subject: 'Welcome!',
    body: '<h1>Welcome to our platform!</h1>'
  }
});

Vector Embeddings (AI)

-- pgvector Extension aktivieren
CREATE EXTENSION IF NOT EXISTS vector;

-- Documents Tabelle mit Embeddings
CREATE TABLE documents (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  content TEXT NOT NULL,
  embedding VECTOR(1536),  -- OpenAI ada-002 Dimension
  metadata JSONB,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Index für schnelle Similarity Search
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops)
  WITH (lists = 100);

-- Similarity Search Funktion
CREATE OR REPLACE FUNCTION search_documents(
  query_embedding VECTOR(1536),
  match_count INT DEFAULT 5
)
RETURNS TABLE (
  id UUID,
  content TEXT,
  similarity FLOAT
) AS $$
BEGIN
  RETURN QUERY
  SELECT
    documents.id,
    documents.content,
    1 - (documents.embedding <=> query_embedding) AS similarity
  FROM documents
  ORDER BY documents.embedding <=> query_embedding
  LIMIT match_count;
END;
$$ LANGUAGE plpgsql;
// Embedding generieren und speichern
async function addDocument(content: string) {
  // Embedding via OpenAI
  const embeddingResponse = await openai.embeddings.create({
    model: 'text-embedding-ada-002',
    input: content
  });

  const embedding = embeddingResponse.data[0].embedding;

  // In Supabase speichern
  const { data, error } = await supabase
    .from('documents')
    .insert({ content, embedding })
    .select()
    .single();

  return data;
}

// Similarity Search
async function searchDocuments(query: string) {
  // Query Embedding
  const embeddingResponse = await openai.embeddings.create({
    model: 'text-embedding-ada-002',
    input: query
  });

  const queryEmbedding = embeddingResponse.data[0].embedding;

  // Search via RPC
  const { data, error } = await supabase.rpc('search_documents', {
    query_embedding: queryEmbedding,
    match_count: 5
  });

  return data;
}

Fazit

Supabase bietet 2026:

  1. PostgreSQL Power: Enterprise-DB mit RLS, Triggers, Functions
  2. Realtime Built-in: CDC, Broadcast, Presence über WebSockets
  3. Edge Functions: Deno-basiert, global verteilt
  4. Vector Search: pgvector für AI/RAG-Anwendungen

Eine vollständige Backend-Plattform für moderne Anwendungen.


Bildprompts

  1. "Database and real-time connections flowing together, Supabase architecture visualization"
  2. "PostgreSQL elephant with real-time lightning bolts, modern database concept"
  3. "Edge functions distributed globally on world map, serverless infrastructure"

Quellen