Design wechseln

Supabase Einstieg: PostgreSQL + Auth + Storage als All-in-One-Backend

Easton editorial illustration: realtime data exchange hub

Ich starre auf den zwölften Datenbankverbindungsfehler auf dem Bildschirm und merke: Als Frontend-Entwickler ein Full-Stack-Projekt zu bauen, ist wirklich schwer.

Früher musste man für jedes Backend-Feature Node.js, Express, Datenbankkonfiguration, Benutzerauthentifizierung und Dateispeicher lernen – jedes davon eine eigene Hürde. Bis Supabase auftauchte.

Kurz gesagt: Supabase ist die Open-Source-Alternative zu Firebase – aber mit PostgreSQL statt dem oft frustrierenden NoSQL. Datenbank, Authentifizierung und Dateispeicher sind in einem Paket gebündelt: Backend as a Service aus einer Hand.

Dieser Artikel führt Sie von Grund auf durch Supabase und konzentriert sich auf die drei Kernfunktionen: Database, Auth und Storage. Danach können Sie damit ein vollständiges Backend schnell aufsetzen – ohne sich in Konfigurationsdateien zu verlieren.


Was ist Supabase?

Zuerst die Grundfrage: Was ist Supabase eigentlich?

Es ist eine BaaS-Plattform – Backend as a Service. Das heißt: Sie müssen keinen eigenen Server betreiben, keine Datenbank konfigurieren und keine API schreiben – Supabase übernimmt das.

Anders als Firebase ist Supabase vollständig Open Source. Und es nutzt PostgreSQL – das ist wichtig. PostgreSQL ist eine relationale Datenbank: komplexe SQL-Abfragen, klare Datenbeziehungen – deutlich angenehmer als dokumentenorientierte Datenbanken wie Firestore.

Supabase bietet sechs Kernfunktionen:

  • Database: PostgreSQL-Datenbank mit SQL-Abfragen und automatisch generierter REST-API
  • Auth: Benutzerauthentifizierung mit E-Mail-Login, Social Login (Google, GitHub usw.) und JWT Token
  • Storage: Dateispeicher ähnlich AWS S3, aber einfacher
  • Realtime: Echtzeit-Datensynchronisation – ideal für Chat-Apps
  • Edge Functions: Edge-Computing ähnlich AWS Lambda
  • Vector Database: Vektordatenbank – ideal für KI-Anwendungen

In diesem Artikel geht es nur um die ersten drei: Database, Auth und Storage – das Herzstück. Die übrigen Funktionen behandeln wir später.

Vielleicht fragen Sie sich: Open Source, PostgreSQL, Auth und Storage – wie schlägt sich Supabase gegen Firebase? Das kommt ausführlich weiter unten. Kurz: Bei SQL-Abfragen, komplexen Beziehungen oder voller Datenkontrolle → Supabase. Bei Mobile-First und besonders starker Echtzeit-Sync → Firebase.


Schnellstart

Supabase-Projekt erstellen

Schritt eins: Registrieren Sie sich auf supabase.com. Danach klicken Sie auf „New Project“, um ein neues Projekt anzulegen.

Beim Anlegen benötigen Sie:

  • Projektname: beliebig, z. B. my-first-app
  • Datenbankpasswort: unbedingt notieren – wird später gebraucht
  • Region: nächstgelegene wählen; für Asien Singapore oder Tokyo

In etwa drei Minuten ist das Projekt bereit. Im Dashboard sehen Sie links viele Menüpunkte: Table Editor, Authentication, Storage, Edge Functions … Keine Sorge – dieser Artikel führt Sie Schritt für Schritt durch.

Installation und Initialisierung

Als Nächstes Supabase im Frontend-Projekt einbinden. Abhängigkeit installieren:

npm install @supabase/supabase-js

Im Supabase Dashboard benötigen Sie zwei Werte: Project URL und Anon Public Key – unter Settings > API.

Supabase Client initialisieren:

import { createClient } from '@supabase/supabase-js';

const supabaseUrl = 'https://your-project-id.supabase.co';
const supabaseAnonKey = 'your-anon-public-key';

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

Dieser Code ist Ihr Einstiegspunkt zu Supabase. Alle weiteren Operationen – Datenbankabfragen, Login, Datei-Upload – laufen über das supabase-Objekt.

Verbindungstest

Nach der Initialisierung kurz prüfen, ob die Verbindung steht. Am einfachsten: eine Abfrage auf die Datenbank:

const { data, error } = await supabase.from('users').select('*');

if (error) {
  console.error('Verbindung fehlgeschlagen:', error.message);
} else {
  console.log('Verbindung erfolgreich!', data);
}

Fehlt die Tabelle users, ist das normal – die legen Sie im nächsten Abschnitt an.

Typische Fehler:

  • URL oder Key falsch kopiert – vollständige Werte prüfen
  • CORS-Fehler – selten nötig, Supabase erlaubt Frontend-Zugriff standardmäßig
  • Netzwerkprobleme – gelegentlich; ein paar Minuten warten und erneut versuchen

Bei Erfolg erhalten Sie Daten zurück – oft ein leeres Array [], wenn die Tabelle noch leer ist.


Database: Datenbankoperationen

Das ist das Herz von Supabase. Wer PostgreSQL kennt, fühlt sich heimisch; als Frontend-Entwickler ohne SQL-Erfahrung hilft der visuelle Table Editor – Datenbankoperationen ohne SQL sind möglich.

Datentabelle anlegen

Zwei Wege: Table Editor (visuell) oder SQL.

Table Editor: Links „Table Editor“ wählen, dann „Create a new table“. Tabellenname, Feldnamen und Feldtypen eingeben.

Beispiel – Tabelle users:

FeldnameTypEinschränkung
idint8Primary Key, Auto Increment
emailtextUnique, Not Null
nametext-
created_attimestamptzDefault: now()

Mit „Save“ ist die Tabelle erstellt.

Persönlich finde ich SQL schneller – besonders für komplexere Operationen später. Den SQL Editor finden Sie links im Dashboard.

SQL für users- und projects-Tabellen:

-- Nutzertabelle
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  email TEXT UNIQUE NOT NULL,
  name TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Projekt-Tabelle
CREATE TABLE projects (
  id SERIAL PRIMARY KEY,
  user_id INTEGER REFERENCES users(id),
  title TEXT NOT NULL,
  description TEXT,
  status TEXT DEFAULT 'active',
  created_at TIMESTAMPTZ DEFAULT NOW()
);

Wichtiges Detail: user_id in projects ist ein Foreign Key zu users.id. Jedes Projekt gehört einem Nutzer – der Vorteil relationaler Datenbanken: klare Beziehungen.

CRUD-Operationen

Tabellen stehen – jetzt Create, Read, Update, Delete testen.

Daten einfügen:

// Nutzer einfügen
const { data, error } = await supabase
  .from('users')
  .insert([
    { email: '[email protected]', name: 'John Doe' }
  ]);

if (error) {
  console.error('Einfügen fehlgeschlagen:', error.message);
} else {
  console.log('Einfügen erfolgreich:', data);
}

Daten abfragen:

// Alle Projekte mit zugehörigen Nutzerdaten
const { data, error } = await supabase
  .from('projects')
  .select(`
    *,
    users (
      name,
      email
    )
  `)
  .eq('status', 'active')
  .order('created_at', { ascending: false });

console.log(data);

Diese Abfrage holt nicht nur projects, sondern über den Foreign Key auch die verknüpften Nutzerdaten – eine Abfrage, zwei Tabellen. In Firestore wären mehrere Runden und manuelles Zusammenführen nötig.

Daten aktualisieren:

const { data, error } = await supabase
  .from('projects')
  .update({ status: 'completed' })
  .eq('id', 1);

Daten löschen:

const { data, error } = await supabase
  .from('projects')
  .delete()
  .eq('id', 1);

Die Operationen sind intuitiv. .eq() bedeutet „gleich“. Weitere Filter: .gt() (größer), .lt() (kleiner), .like() (Muster) – die gängigen SQL-Filter sind abgedeckt.

Row Level Security (RLS)

Besonders wichtig bei nutzerbezogenen Funktionen.

RLS steht für Row Level Security – Zeilenebenen-Sicherheit. Sie steuern, dass Nutzer nur eigene Daten sehen, nicht fremde.

Beispiel: In projects liegen viele Einträge – nach dem Login soll jeder nur eigene Projekte sehen.

RLS aktivieren:

ALTER TABLE projects ENABLE ROW LEVEL SECURITY;

Policy anlegen:

-- Nutzer sehen nur eigene Projekte
CREATE POLICY "Users can view their own projects"
ON projects FOR SELECT
USING (user_id = auth.uid());

auth.uid() ist eine Supabase-Funktion und liefert die ID des angemeldeten Nutzers. Diese Policy bedeutet: Beim SELECT auf projects nur Zeilen mit user_id = aktuelle Nutzer-ID.

Analog Policies für INSERT, UPDATE und DELETE:

-- Nutzer dürfen nur eigene Projekte einfügen
CREATE POLICY "Users can insert their own projects"
ON projects FOR INSERT
WITH CHECK (user_id = auth.uid());

-- Nutzer dürfen nur eigene Projekte aktualisieren
CREATE POLICY "Users can update their own projects"
ON projects FOR UPDATE
USING (user_id = auth.uid())
WITH CHECK (user_id = auth.uid());

-- Nutzer dürfen nur eigene Projekte löschen
CREATE POLICY "Users can delete their own projects"
ON projects FOR DELETE
USING (user_id = auth.uid());

Selbst bei böswilligen Abfragen fremder Daten blockiert die Datenbank – deutlich sicherer als Prüfungen im Frontend.


Auth: Benutzerauthentifizierung

Supabase Auth ist umfangreich: E-Mail/Passwort, passwortloser Login (Magic Link), OTP, Social Login (Google, GitHub, Apple und 20+ Plattformen), Telefon-Login und Enterprise-SSO.

Überblick über die Authentifizierung

Kernmechanismus: JWT Token – JSON Web Token. Nach erfolgreichem Login erzeugt Supabase ein Token; das Frontend sendet es bei API-Anfragen mit, das Backend prüft die Identität.

Supabase Auth speichert Nutzerdaten automatisch in der PostgreSQL-Tabelle auth.users – diese Tabelle existiert von Anfang an. Nutzer-ID, E-Mail, Erstellungszeit, letzter Login – alles dort.

Session Persistence: Nach dem Login merkt sich der Browser den Zustand; beim nächsten Besuch kein erneuter Login nötig. Das Supabase-Frontend-SDK übernimmt das automatisch.

Email/Password-Authentifizierung

Die häufigste Methode. Zuerst Registrierung:

// Nutzer registrieren
const { data, error } = await supabase.auth.signUp({
  email: '[email protected]',
  password: 'securepassword123',
});

if (error) {
  console.error('Registrierung fehlgeschlagen:', error.message);
} else {
  console.log('Registrierung erfolgreich:', data);
}

Nach der Registrierung sendet Supabase eine Bestätigungs-E-Mail. Klick auf den Link aktiviert das Konto – Standardverhalten, im Dashboard deaktivierbar, aber nicht empfohlen: E-Mail-Bestätigung verhindert Missbrauch.

Anmeldung:

// Nutzer anmelden
const { data, error } = await supabase.auth.signInWithPassword({
  email: '[email protected]',
  password: 'securepassword123',
});

if (error) {
  console.error('Anmeldung fehlgeschlagen:', error.message);
} else {
  console.log('Anmeldung erfolgreich:', data);
}

Nach erfolgreichem Login Nutzerdaten abrufen:

// Aktuell angemeldeten Nutzer abrufen
const { data: { user } } = await supabase.auth.getUser();

console.log('Aktueller Nutzer:', user);
// Ausgabe ähnlich: { id: 'abc123', email: '[email protected]', ... }

Abmeldung:

await supabase.auth.signOut();

Danach ist die Session geleert – erneute Anmeldung nötig.

Passwort zurücksetzen – eingebaute Funktion:

// E-Mail zum Passwort-Reset senden
const { data, error } = await supabase.auth.resetPasswordForEmail(
  '[email protected]'
);

Der Nutzer erhält eine E-Mail und setzt das Passwort über den Link – ohne zusätzliche Logik Ihrerseits.

Social Auth

Beliebt, weil Nutzer ohne Formular mit Google oder GitHub anmelden können.

Konfiguration im Dashboard: Authentication > Providers – gewünschte Plattform aktivieren, z. B. Google oder GitHub.

Pro Plattform ähnliche Schritte:

  1. OAuth App bei Google/GitHub anlegen
  2. Client ID und Client Secret in Supabase eintragen
  3. Redirect URL konfigurieren

Frontend-Code:

// Google-Login
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'google',
});

// GitHub-Login
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'github',
});

Die Seite leitet zu Google/GitHub weiter; nach Zustimmung zurück in Ihre App – Login abgeschlossen.

Callback-URL und Zusatzparameter anpassen:

const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: {
    redirectTo: 'https://your-app.com/dashboard',
    queryParams: {
      access_type: 'offline',
      prompt: 'consent',
    }
  }
});

redirectTo: Zielseite nach erfolgreichem Login. queryParams: zusätzliche OAuth-Parameter, z. B. Offline-Zugriff oder erneute Zustimmung.

RLS mit Auth verbinden

Bei RLS wurde auth.uid() erwähnt – jetzt wird der Zusammenhang klar.

Nach dem Login liefert auth.uid() die Nutzer-ID. In Database-Policies nutzen Sie diese ID für „Nutzer sehen nur eigene Daten“.

Beispiel: Projekt anlegen und automatisch dem aktuellen Nutzer zuordnen.

// Projekt erstellen, automatisch aktuellem Nutzer zuordnen
const { data: { user } } = await supabase.auth.getUser();

const { data, error } = await supabase
  .from('projects')
  .insert([
    {
      title: 'New Project',
      user_id: user.id  // Nutzer-ID aus Auth
    }
  ]);

RLS-Policies stellen sicher:

  • SELECT: nur Projekte mit user_id = auth.uid()
  • INSERT: user_id muss auth.uid() entsprechen
  • UPDATE und DELETE analog

Authentifizierung und Berechtigungen sind damit durchgängig verbunden – Login, Datenzuordnung, Zugriffsbeschränkung in einer Linie.


Storage: Dateispeicher

Supabase Storage ist Objektspeicher ähnlich AWS S3 – deutlich einfacher in der Handhabung. Ideal für Avatare, Bilder und Dokumente.

Überblick Storage

Zwei Kernkonzepte: Bucket und Object.

  • Bucket: Speicherbehälter, vergleichbar mit Ordnern. Mehrere Buckets möglich – z. B. avatars für Profilbilder, documents für Dokumente
  • Object: konkrete Datei, z. B. avatar.jpg, report.pdf

Zwei Berechtigungsmodi:

  • Public: öffentlicher Bucket – jeder kann Dateien abrufen (öffentliche Bilder)
  • Private: privater Bucket – Zugriff nur mit Berechtigung (vertrauliche Dokumente)

Bucket anlegen

Im Dashboard Storage wählen, dann „New Bucket“.

Beispiel avatars für Profilbilder:

  • Name: avatars
  • Public bucket: aktivieren (Avatare sind meist öffentlich)
  • File size limit: z. B. 2 MB

Danach können Dateien hochgeladen werden.

Datei-Upload und -Download

Upload:

// Avatar hochladen
const file = document.getElementById('avatar-input').files[0];

const { data, error } = await supabase.storage
  .from('avatars')
  .upload('user-id/avatar.jpg', file, {
    cacheControl: '3600',
    upsert: false
  });

if (error) {
  console.error('Upload fehlgeschlagen:', error.message);
} else {
  console.log('Upload erfolgreich:', data);
}

Details:

  • Erster Parameter von upload(): Dateipfad 'user-id/avatar.jpg'
  • cacheControl: Cache-Dauer in Sekunden, hier 3600
  • upsert: bei existierender Datei überschreiben? false = Fehler; true = überschreiben

Download:

// Datei herunterladen
const { data, error } = await supabase.storage
  .from('avatars')
  .download('user-id/avatar.jpg');

if (error) {
  console.error('Download fehlgeschlagen:', error.message);
} else {
  // data ist ein Blob – in URL für Anzeige umwandeln
  const url = URL.createObjectURL(data);
  document.getElementById('avatar-img').src = url;
}

Bei öffentlichen Buckets direkt die öffentliche URL:

// Öffentliche URL abrufen
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('user-id/avatar.jpg');

console.log('Öffentliche URL:', data.publicUrl);
// z. B.: https://your-project.supabase.co/storage/v1/object/public/avatars/user-id/avatar.jpg

Private Buckets: temporäre URL erzeugen:

// Temporäre URL (1 Stunde gültig)
const { data, error } = await supabase.storage
  .from('documents')
  .createSignedUrl('private-file.pdf', 3600);

console.log('Temporäre URL:', data.signedUrl);

Datei löschen:

const { data, error } = await supabase.storage
  .from('avatars')
  .remove(['user-id/avatar.jpg']);

Zugriffskontrolle

Storage unterstützt RLS-Policies – steuern, welche Dateien ein Nutzer nutzen darf.

Beispiel: Nutzer dürfen nur in den eigenen Ordner hochladen und Dateien dort abrufen.

-- Nutzer dürfen nur in eigenen Ordner hochladen
CREATE POLICY 'Users can upload to their own folder'
ON storage.objects FOR INSERT
WITH CHECK (
  bucket_id = 'avatars' AND
  (storage.foldername(name))[1] = auth.uid()::text
);

-- Nutzer dürfen nur eigene Dateien abrufen
CREATE POLICY 'Users can access their own files'
ON storage.objects FOR SELECT
USING (
  bucket_id = 'avatars' AND
  (storage.foldername(name))[1] = auth.uid()::text
);

storage.foldername(name) extrahiert den Ordner aus dem Pfad. [1] ist der erste Ordnername – die Nutzer-ID.

Upload-Pfad muss user-id/filename sein – RLS prüft, ob user-id der aktuellen Nutzer-ID entspricht. Sonst wird abgelehnt.


Praxisprojekt: Task-Management-App

Alles zusammen in einem Beispiel.

Einfache Task-Management-App mit:

  • Registrierung und Login
  • Aufgaben anlegen und Liste anzeigen
  • Anhänge hochladen

Datenbankdesign

Tabellenstruktur:

-- Nutzertabelle (von Auth automatisch, nicht manuell anlegen)
-- tasks-Tabelle
CREATE TABLE tasks (
  id SERIAL PRIMARY KEY,
  user_id UUID REFERENCES auth.users(id) NOT NULL,
  title TEXT NOT NULL,
  description TEXT,
  status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'completed')),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- RLS aktivieren
ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;

-- Policy: Nutzer verwalten nur eigene Tasks
CREATE POLICY 'Users can manage their own tasks'
ON tasks FOR ALL
USING (user_id = auth.uid())
WITH CHECK (user_id = auth.uid());

-- attachments-Tabelle (Tasks zugeordnet)
CREATE TABLE task_attachments (
  id SERIAL PRIMARY KEY,
  task_id INTEGER REFERENCES tasks(id) ON DELETE CASCADE,
  file_name TEXT NOT NULL,
  file_path TEXT NOT NULL,
  file_size INTEGER,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- RLS aktivieren
ALTER TABLE task_attachments ENABLE ROW LEVEL SECURITY;

-- Policy: Berechtigung über Task-Zuordnung
CREATE POLICY 'Users can manage attachments of their tasks'
ON task_attachments FOR ALL
USING (
  EXISTS (
    SELECT 1 FROM tasks
    WHERE tasks.id = task_attachments.task_id
    AND tasks.user_id = auth.uid()
  )
);

Bei task_attachments prüft die Policy nicht direkt user_id, sondern über task_id die tasks-Tabelle und deren user_id. Nur der Task-Eigentümer verwaltet Anhänge.

Storage Bucket anlegen

Bucket task-files als privat:

-- Im Dashboard oder per SQL
INSERT INTO storage.buckets (name, public)
VALUES ('task-files', false);

-- Policy: Upload nur in eigenen Ordner
CREATE POLICY 'Users can upload task files'
ON storage.objects FOR INSERT
WITH CHECK (
  bucket_id = 'task-files' AND
  (storage.foldername(name))[1] = auth.uid()::text
);

CREATE POLICY 'Users can access task files'
ON storage.objects FOR SELECT
USING (
  bucket_id = 'task-files' AND
  (storage.foldername(name))[1] = auth.uid()::text
);

Kernimplementierung

Alles zusammengeführt:

import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  'https://your-project.supabase.co',
  'your-anon-key'
);

// 1. Registrierung
async function register(email: string, password: string) {
  const { data, error } = await supabase.auth.signUp({
    email,
    password,
  });

  if (error) throw error;
  return data;
}

// 2. Anmeldung
async function login(email: string, password: string) {
  const { data, error } = await supabase.auth.signInWithPassword({
    email,
    password,
  });

  if (error) throw error;
  return data;
}

// 3. Task erstellen
async function createTask(title: string, description?: string) {
  const { data: { user } } = await supabase.auth.getUser();

  if (!user) throw new Error('Nicht angemeldet');

  const { data, error } = await supabase
    .from('tasks')
    .insert([
      {
        title,
        description,
        user_id: user.id,
      }
    ])
    .select();

  if (error) throw error;
  return data[0];
}

// 4. Task-Liste abrufen
async function getTasks() {
  const { data, error } = await supabase
    .from('tasks')
    .select('*')
    .order('created_at', { ascending: false });

  if (error) throw error;
  return data;
}

// 5. Anhang hochladen
async function uploadAttachment(taskId: number, file: File) {
  const { data: { user } } = await supabase.auth.getUser();

  if (!user) throw new Error('Nicht angemeldet');

  const filePath = user.id + '/' + taskId + '/' + file.name;

  const { data, error } = await supabase.storage
    .from('task-files')
    .upload(filePath, file);

  if (error) throw error;

  // In task_attachments speichern
  const { data: attachment, error: dbError } = await supabase
    .from('task_attachments')
    .insert([
      {
        task_id: taskId,
        file_name: file.name,
        file_path: filePath,
        file_size: file.size,
      }
    ])
    .select();

  if (dbError) throw dbError;

  return attachment[0];
}

// 6. Task-Anhänge abrufen
async function getTaskAttachments(taskId: number) {
  const { data, error } = await supabase
    .from('task_attachments')
    .select('*')
    .eq('task_id', taskId);

  if (error) throw error;

  // Temporäre Zugriffs-URLs erzeugen
  const attachmentsWithURLs = data.map(async (attachment) => {
    const { data: urlData } = await supabase.storage
      .from('task-files')
      .createSignedUrl(attachment.file_path, 3600);

    return {
      ...attachment,
      url: urlData.signedUrl,
    };
  });

  return Promise.all(attachmentsWithURLs);
}

// 7. Task abschließen
async function completeTask(taskId: number) {
  const { data, error } = await supabase
    .from('tasks')
    .update({ status: 'completed', updated_at: new Date() })
    .eq('id', taskId)
    .select();

  if (error) throw error;
  return data[0];
}

// 8. Abmelden
async function logout() {
  await supabase.auth.signOut();
}

Registrierung, Login, Task-CRUD, Datei-Upload – das Grundgerüst einer vollständigen App. Darauf bauen Sie Kategorien, Tags, Kommentare, Benachrichtigungen usw. auf.


Supabase vs. Firebase im Vergleich

Supabase oder Firebase? Hier der direkte Vergleich.

Zentrale Unterschiede

VergleichsdimensionSupabaseFirebase
DatenbanktypPostgreSQL (relational)Firestore (Dokument-NoSQL)
Open SourceVollständig Open SourceGoogle-Proprietärprodukt
PreismodellFest $25/Monat (Pro Plan)Nutzungsbasiert (Read/Write/Storage getrennt)
Dateneigentum100 % bei Ihnen, jederzeit exportierbarDaten bei Google, Export umständlich
AbfragefähigkeitMächtiges SQL, komplexe JoinsEingeschränkt, komplexe Beziehungen brauchen mehrere Abfragen
EchtzeitWebSocket, manuelles AbonnementFirestore nativ Echtzeit, automatische Sync
OfflineEigenes Caching nötigNative Offline-Unterstützung
MigrationStandard-PostgreSQL, einfachProprietäres Format, hohe Kosten

Empfehlungen nach Szenario

Supabase wählen, wenn:

  • Komplexe Datenbeziehungen und SQL-Abfragen nötig sind
  • Langzeitprojekt mit voller Datenkontrolle
  • Team kennt SQL und möchte PostgreSQL
  • Kostenbewusst – fester Preis stabiler als Nutzungsabrechnung
  • Open Source und kontrollierbarer Stack wichtig sind

Firebase wählen, wenn:

  • Schneller Prototyp unter Zeitdruck
  • Mobile-First, Offline zentral
  • Echtzeit-Sync Kernfunktion (Chat, Kollaboration)
  • Bereits Google-Cloud-Ökosystem im Einsatz
  • Einfache Datenstruktur, wenig komplexe Abfragen

Persönliche Erfahrung: Bei Firebase-Projekten kam die Rechnung einmal auf über $800/Monat. Nach Migration zu Supabase: gleiche Funktionen, etwa $25/Monat – ein großer Unterschied.

Firebase-Datenexport ist mühsam – Firestore-Format ist proprietär, Konvertierung nötig. Supabase: SQL-Export oder CSV über PostgreSQL-Tools – jederzeit Migration zu anderer Datenbank möglich.

Firebase ist bei Echtzeit und Mobile stärker – Firestore-Sync und Offline gut ausgereift. Für Chat oder Kollaborationsdokumente mit starkem Echtzeitbedarf kann Firebase passender sein.


Fazit und Empfehlungen

Supabase im Kern:

  • PostgreSQL – mächtige Abfragen, klare Beziehungen
  • Enterprise-Authentifizierung – viele Login-Methoden, JWT, RLS
  • Objektspeicher – einfach, mit Zugriffskontrolle
  • Echtzeit, Edge Functions – zum späteren Vertiefen

Open Source, volle Datenkontrolle, stabile Kosten – wichtig für Langzeitprojekte.

Als Frontend-Entwickler, der schnell Full-Stack bauen will ohne Backend-Konfigurationsstress, ist Supabase eine starke Wahl. Database, Auth und Storage in wenigen Stunden – deutlich schneller als klassische Backend-Entwicklung.

Lernempfehlungen:

  • Zuerst Database, Auth und Storage – das Fundament
  • Dann RLS-Policies – verbindet Authentifizierung und Berechtigungen
  • Bestes Lernmittel: echtes Projekt von Grund auf
  • Später Realtime, Edge Functions, Vector Database vertiefen

Die offizielle Dokumentation ist klar geschrieben. Auf GitHub finden Sie awesome-supabase mit vielen Ressourcen.

Kurz: Wenn Sie PostgreSQL als Backend wollen, ohne sich in Konfiguration zu verlieren – Supabase legt den Weg frei. Einfach loslegen.

Supabase – die drei Kernfunktionen schnell einrichten

Supabase-Projekt von Grund auf aufsetzen und Database, Auth sowie Storage beherrschen

⏱️ Estimated time: 2 hr

  1. 1

    Step 1: Supabase-Projekt erstellen

    Registrieren Sie sich auf supabase.com und legen Sie ein neues Projekt an:

    • Projektname: my-first-app
    • Datenbankpasswort: unbedingt notieren
    • Region: nächstgelegene wählen (für Asien: Singapore oder Tokyo)

    Nach der Erstellung finden Sie unter Settings > API die Project URL und den Anon Public Key
  2. 2

    Step 2: Client installieren und initialisieren

    Abhängigkeit im Frontend-Projekt installieren:

    npm install @supabase/supabase-js

    Supabase Client initialisieren:

    import { createClient } from '@supabase/supabase-js';

    const supabase = createClient(
    'https://your-project.supabase.co',
    'your-anon-key'
    );
  3. 3

    Step 3: Datentabelle anlegen und RLS konfigurieren

    Tabelle im SQL Editor erstellen:

    CREATE TABLE tasks (
    id SERIAL PRIMARY KEY,
    user_id UUID REFERENCES auth.users(id),
    title TEXT NOT NULL,
    status TEXT DEFAULT 'pending'
    );

    ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;

    CREATE POLICY 'Users can manage their own tasks'
    ON tasks FOR ALL
    USING (user_id = auth.uid());

    Die RLS-Policy stellt sicher, dass Nutzer nur eigene Daten sehen
  4. 4

    Step 4: Benutzerauthentifizierung implementieren

    Beispiel für Email/Password-Authentifizierung:

    // Registrierung
    await supabase.auth.signUp({
    email: '[email protected]',
    password: 'password123'
    });

    // Anmeldung
    await supabase.auth.signInWithPassword({
    email: '[email protected]',
    password: 'password123'
    });

    // Aktuellen Nutzer abrufen
    const { data: { user } } = await supabase.auth.getUser();

    Social Login über Google, GitHub und 20+ weitere Plattformen unterstützt
  5. 5

    Step 5: Dateispeicher konfigurieren

    Storage Bucket anlegen (öffentlich oder privat):

    // Datei hochladen
    await supabase.storage
    .from('avatars')
    .upload('user-id/avatar.jpg', file);

    // Öffentliche URL abrufen
    const { data } = supabase.storage
    .from('avatars')
    .getPublicUrl('user-id/avatar.jpg');

    Für private Dateien createSignedUrl für temporäre Zugriffslinks nutzen

FAQ

Was sind die zentralen Unterschiede zwischen Supabase und Firebase?
Supabase nutzt PostgreSQL als relationale Datenbank mit komplexen SQL-Abfragen und Datenbeziehungen; Firebase setzt auf Firestore als dokumentenorientiertes NoSQL mit eingeschränkten Abfragemöglichkeiten. Supabase ist vollständig Open Source, kostet fest $25/Monat und bietet 100 % Datenkontrolle; Firebase ist proprietär, nutzungsbasiert (bis $800+/Monat möglich) und erschwert den Datenexport.
Für welche Projekte eignet sich Supabase?
Besonders geeignet für:

• Komplexe Datenbeziehungen mit SQL-Abfragen und Joins
• Langzeitprojekte mit voller Datenkontrolle
• Kostenbewusste Teams mit begrenztem Budget (fester Preis stabiler)
• Frontend-Entwickler, die schnell Full-Stack-Apps bauen
• Open-Source-Priorität und kontrollierbarer Tech-Stack
Was ist Row Level Security (RLS) und wofür braucht man sie?
RLS ist eine Zeilenebenen-Sicherheitsfunktion von PostgreSQL: Auf Datenbankebene steuern Sie, dass Nutzer nur eigene Datensätze sehen. Mit auth.uid() aus Supabase Auth greifen angemeldete Nutzer ausschließlich auf selbst erstellte Einträge zu – deutlich sicherer als Prüfungen im Frontend.
Welche Anmeldemethoden unterstützt Supabase Auth?
Unterstützte Methoden:

• Email/Password (E-Mail und Passwort)
• Magic Link (passwortlose Anmeldung)
• OTP (Einmalpasswort)
• Social Auth (Google, GitHub, Apple und 20+ Plattformen)
• Phone Auth (Twilio, MessageBird)
• SSO (Enterprise Single Sign-On)
Was ist der Unterschied zwischen öffentlichen und privaten Storage-Buckets?
Öffentliche Buckets (Public Bucket): Dateien für jeden zugänglich – geeignet für öffentliche Bilder, Avatare usw. Private Buckets (Private Bucket): Zugriff nur mit Berechtigung; createSignedUrl erzeugt temporäre Links (mit Ablaufzeit) – ideal für vertrauliche Dokumente und Nutzer-Uploads.
Wie lange dauert die Migration von Firebase zu Supabase?
Einfache Apps: 2–3 Tage – Firestore-Daten in PostgreSQL-Tabellen, Firebase-Auth-Nutzer nach Supabase Auth, Cloud-Storage-Dateien nach Supabase Storage. Komplexe Apps: 1–2 Wochen, vor allem wegen Datenmodell-Konvertierung und Abfrage-Logik.
Was bietet der kostenlose Supabase-Plan?
Der Free Plan umfasst: unbegrenzte API-Anfragen, 50.000 monatlich aktive Nutzer, 500 MB Datenbankspeicher, 1 GB Dateispeicher, 5 GB Bandbreite. Geeignet für persönliche MVPs; bei Überschreitung Upgrade auf Pro Plan ($25/Monat).

14 Min. Lesezeit · Veröffentlicht am: 3. Apr. 2026 · Aktualisiert am: 14. Juli 2026

Kommentare

Melde dich mit GitHub an, um einen Kommentar zu hinterlassen

Easton BlogEaston Blog