BERENT.AI — Beratung + Entwicklung
KI-Masterclass · KOERTING Institute

Deine Infrastruktur
bekommt ein Gedächtnis

Am 27. Juli haben wir deine eigene Infrastruktur aufgebaut, VPS, Coolify, GitHub, und ein Hello World ins Netz gestellt. Heute kommt der Baustein dazu, der aus einer Seite eine Anwendung macht: eine Datenbank. Und Linear als System für alles, was danach noch kommt.

Zu groß gedacht, und am Abend läuft nichts. Nimm etwas Kleines, das dich wirklich betrifft. Ausbauen kannst du ab morgen.

Was heute liegen bleibt, nimmst du als Liste mit.

Online · 2 Stunden · Marcus Kunkel

Erst lesen, dann loslegen

Dies hier ist kein Wettbewerb. Die Basis ist das vollständige Lernziel dieser Session. Wer die Basis schafft, hat alles erreicht, worum es geht, und erhält heute Abend eine funktionierende Anwendung.

Plus und Kür sind für alle, die schneller fertig sind. Sie sind aufklappbar, damit sie niemanden ablenken. Es ist ausdrücklich in Ordnung, sie heute nicht zu öffnen.

Wo läuft eigentlich was

Die Frage kommt stets, deshalb vorweg. Es sind drei getrennte Orte:

Rechner und Datenbank werden über zwei Werte in einer Datei namens .env verbunden. Heute arbeiten wir auf deinem Rechner, der Weg auf den VPS steht am Ende als Ausblick.

+ + +

Vorab · einmalig

Vorab, bitte vor der Session

Ohne die drei Konten unten funktioniert die Session nicht. Claude Code, Supabase, Linear. Das gilt live und wenn du die Seite später allein durchgehst. Wenn etwas klemmt, melde dich rechtzeitig.

1 · Claude Code startklar

Claude Code muss laufen, und du solltest dich einmal eingeloggt haben. Prüfe dabei dein Kontingent. Wir arbeiten mit Opus 5 (falls möglich, stets auf Max), und das verbraucht spürbar.

2 · Supabase-Konto anlegen

Auf supabase.com registrieren. Kostenlos, Anmeldung über GitHub geht am schnellsten. Noch kein Projekt anlegen, das machen wir gemeinsam.

3 · Linear-Konto

Auf linear.app registrieren, falls noch nicht geschehen. Wir nutzen es in der Session als Dokumentation und Arbeitsliste (Backlog).

4 · Die Sitzung des 27. Juli wiederfinden

Suche die Claude-Code-Sitzung heraus, in der wir VPS, Coolify und GitHub eingerichtet haben. Öffne sie einmal und lass sie geöffnet. Wir benötigen daraus gleich ein Dokument.

Falls du die alte Sitzung nicht mehr hast: Das gilt auch, falls du am 27. Juli nicht dabei warst oder gerade kein Kontingent mehr hast. In Schritt 02 steht eine Ersatzlösung. Kein Grund fernzubleiben.
01

Dein Anwendungsfall

ca. 17 Minuten

Nicht jede Anwendung braucht eine Datenbank. Sie braucht eine, sobald Daten länger leben sollen als ein Seitenaufruf, oder sobald mehr als eine Person darauf zugreift. Beides trifft auf das zu, was du heute baust.

Es gibt kein vorgegebenes Thema. Du wählst jetzt, woran du die nächsten zwei Stunden arbeitest.

Impulse falls du noch unentschlossen bist
  • Kontakt- und Netzwerkverwaltung
  • Angebots- und Leistungskatalog
  • Feedback- oder Anmeldeformular mit Auswertung
  • Wissenssammlung, etwa Prompts, Quellen, Notizen
  • Projekt- oder Auftragsübersicht
  • Matching zwischen Anbietern und Anfragen
  • Terminanfragen

Diese Liste ist Inspiration, keine Auswahl. Wer etwas Eigenes hat, nimmt das.

Kriterien für einen Anwendungsfall, der in zwei Stunden trägt

Der Anwendungsfall darf klein sein … und echt. Ausbauen kannst du ab morgen.

Deine Vorlage als intent.md

Der Engpass beim Bauen mit KI ist nicht das Werkzeug. Es ist, wie genau du sagen kannst, was du willst. Wer den Intent aufschreibt, statt ihn zu tippen, kann ihn überarbeiten. Und die Datei trägt beim nächsten Projekt genauso.

Öffne dafür einen leeren Texteditor. Noch kein anderes Werkzeug. Speichere die Datei als intent.md. Später liegt sie neben architektur.md im Projektordner. In Schritt 03 kommt ihr Inhalt in den Startprompt.

Ich moechte [WAS] verwalten.
Die wichtigsten Angaben: [FELD 1], [FELD 2], [FELD 3], ...
Anlegen und als Tabelle anzeigen reicht als Umfang.
Das Ziel: [was es dir nutzt]

Ausgefüllte Blaupause

Unentschlossen? Nimm dieses Beispiel eins zu eins. Es wird die ganze Seite hindurch weitergeführt, mit fertigem SQL in Schritt 04.

Ich moechte meine beruflichen Kontakte verwalten.
Die wichtigsten Angaben: Vorname, Nachname, Firma, E-Mail, Telefon, Notiz.
Anlegen und als Tabelle anzeigen reicht als Umfang.
Das Ziel: Ich sehe auf einen Blick, wen ich kenne und worueber wir
gesprochen haben.

Linear · zwei Issues

ca. 5 Minuten

Der Intent steht. Jetzt das erste Werkzeug.

Leg ein Projekt an, benannt nach dem WAS aus deiner intent.md. Ein Wort oder zwei, kein Satz. Kein neues Team: Linear hat bei der Registrierung schon Workspace und Team angelegt. Der Name, den du dort vergeben hast, bestimmt Team und Kürzel. Wer das Kürzel ändern will, findet es in den Team-Einstellungen unter General, muss es aber nicht. Issue-Nummern vergibt Linear selbst, von KÜRZEL-1 aufsteigend. Du tippst nie eine Nummer. Im kostenlosen Plan sind zwei Teams möglich, Projekte unbegrenzt. Der Anwendungsfall wird als Projekt in diesem Team angelegt.

Genau zwei Issues:

Ein erledigter Eintrag und ein offener. Mehr braucht ein Backlog nicht, um verständlich zu sein.

Linear Issue „Intent steht" auf Done.
02

Neue Sitzung vorbereiten

ca. 8 Minuten

Weshalb eine neue Sitzung und nicht die alte: Die Sitzung des 27. Juli ist voll mit Server-Einrichtung. Für ein neues Vorhaben braucht es einen frischen Ordner und einen frischen Kontext, sonst arbeitet Claude Code gegen alten Ballast an.

Verloren geht dabei nichts. Bevor wir die alte Sitzung schließen, holen wir das Wissen heraus, das wir heute benötigen.

1 · In der alten Sitzung: das Wissen sichern

Öffne die Sitzung des 27. Juli und gib dort diesen Auftrag:

Fasse die Infrastruktur zusammen, die wir hier aufgebaut haben.
Schreibe eine Datei architektur.md, die beschreibt: welcher VPS bei
welchem Anbieter, wie Coolify konfiguriert ist, wie das
GitHub-Repository angebunden ist und wie ein Deployment ablaeuft.
Schreibe sie so, dass eine andere KI-Sitzung damit sofort arbeiten
kann, ohne diesen Chat zu kennen.

2 · Neuen Ordner anlegen

Leg einen neuen Ordner an, benannt nach dem WAS aus deiner intent.md. Kopiere architektur.md und intent.md hinein.

3 · Neue Sitzung starten

Claude Code Desktop öffnen, neue Sitzung, diesen Ordner wählen, Modell Opus 5, Plan Max. Mehr passiert hier noch nicht, der erste Auftrag kommt in Schritt 03.

Falls du die alte Sitzung nicht hast: Nimm die Vorlage unten, füll sie mit deinen eigenen Angaben und speichere sie als architektur.md in deinem neuen Ordner. Oder arbeite zunächst ganz ohne Architekturdokument weiter. Für die heutige Session ist es nicht zwingend, es macht später beim Deployment den Unterschied.
# Architektur

## Server
Anbieter: [z. B. Hostinger]
Produkt und Groesse: [z. B. KVM 2, 2 vCPU, 8 GB RAM, 100 GB SSD]
Betriebssystem: [z. B. Ubuntu 24.04 LTS]
Adresse oder IP: [z. B. 203.0.113.10]

## Coolify
Oberflaeche erreichbar unter: [z. B. http://203.0.113.10:8000]
Bereits angelegte Anwendung: [z. B. hello-world]
Zugewiesene Domain: [z. B. hello.meine-domain.de]

## GitHub
Repository: [z. B. meinname/hello-world]
Anbindung an Coolify: [z. B. ueber die Coolify-GitHub-App]

## Deployment
So kommt eine Aenderung ins Netz: [z. B. Push auf main, Coolify baut
automatisch neu, nach etwa einer Minute ist die Domain aktuell]
03

Der Startprompt

ca. 12 Minuten

Der erste Auftrag in einer neuen Sitzung entscheidet über den ganzen Abend. Deshalb schauen wir uns an, woraus er besteht, bevor du ihn abschickst. Er hat vier Teile:

Lies zuerst architektur.md und intent.md in diesem Ordner.

Jetzt moechte ich einen naechsten Schritt gehen und zwei Bausteine
ergaenzen: Supabase als Datenbank und Linear als System fuer meinen
Backlog. Die Anwendung selbst baust du als Vite-Projekt mit React
in diesem Ordner.

Mein Anwendungsfall steht in intent.md. Hier der Inhalt:
[HIER DEN INHALT AUS intent.md EINSETZEN]

Die Anwendung muss Eintraege anlegen und als Tabelle anzeigen,
mit einer Spalte je Feld.

Fuehre mich bitte 1:1 und sehr konkret durch den Prozess. Ich habe
noch nie selbst Code geschrieben. Erklaere mir bei jedem Schritt
kurz, was passiert und weshalb, bevor du ihn ausfuehrst.

Fang mit meiner Feldliste an: pruefe sie, ergaenze, was fehlt, und
streiche, was zu viel ist. Begruende beides kurz und warte auf meine
Bestaetigung, bevor du die Tabelle anlegst.

Die beiden letzten Absätze sind der eigentliche Kern. Ohne sie baut Claude Code fünfzehn Minuten am Stück durch, am Ende läuft etwas, und du verstehst nichts davon. Mit ihnen hältst du das Tempo in der Hand und siehst jeden Schritt einzeln.

Das ist das Lernziel dieses Abends, nicht das SQL.

Parallel dazu: das Supabase-Projekt

Während Claude Code deine Feldliste prüft, legst du im Browser das Projekt an. In Supabase auf New project, Namen vergeben, Datenbank-Passwort setzen, als Region Frankfurt wählen. Das Passwort benötigst du heute nicht, notiere es trotzdem. Die Einrichtung dauert etwa eine Minute.

Danach auf Project Settings, dort API. Zwei Werte stehen dort:

Finger weg vom service_role key. Der steht auf derselben Seite und umgeht alle Schutzregeln. Er gehört niemals in eine Anwendung und niemals in einen Chat. Wir brauchen ausschließlich den anon-Schlüssel.

Beide Werte fügst du direkt in den Claude-Chat ein, mit einem Satz wie: „Das sind meine Supabase-Zugangsdaten, du brauchst sie gleich." Kein Zwischenspeichern in Notizen oder Dateien. Der anon key ist für die Öffentlichkeit bestimmt und darf dort stehen.

Während Claude arbeitet: Die Datenbank ist zu, nicht offen

ca. 5 Minuten

Nach dem Abschicken des Startprompts arbeitet Claude. Diese Zeit nutzen wir.

Eine Tabelle mit aktivierter Zugriffssperre gibt nichts heraus, bis eine Regel es erlaubt. Wer das nicht weiß, sucht den Fehler in seiner Anwendung, obwohl er in der Datenbank sitzt.

Gleich taucht in der .env das Präfix VITE_ auf. Beides funktioniert gleich: Standardmäßig ist nichts freigegeben. Freigabe passiert ausdrücklich.

04

Die Datenbank entsteht

ca. 30 Minuten

Dieser Schritt hat eine feste Reihenfolge. Claude Code führt, diese Seite hält die Spur.

1 · Projektgerüst

Claude legt das Vite-Projekt an, lädt die Abhängigkeiten und startet npm run dev. Im Terminal steht danach eine Adresse, meist http://localhost:5173. Öffne sie im Browser. Das ist deine Anwendung, noch ohne Daten.

Terminal offen lassen. Solange dort npm run dev läuft, läuft deine Seite. Schließt du das Fenster, ist sie weg. Für weitere Befehle öffnest du ein zweites Terminal.

2 · Die .env, angelegt durch Claude

Du öffnest keinen Texteditor. Der Dateiname beginnt mit einem Punkt und wird von Finder und Explorer ausgeblendet. TextEdit speichert als RTF, der Windows-Editor hängt .txt an. Aus .env wird .env.txt, und nichts funktioniert.

Stattdessen dieser Auftrag an Claude:

Lege eine .env im Projektordner an mit VITE_SUPABASE_URL und
VITE_SUPABASE_ANON_KEY, trage die Werte von oben ein, ergaenze .env
in der .gitignore und zeige mir danach den Inhalt der Datei.

Den Inhalt sollst du sehen, nicht schreiben müssen.

Vite reicht nur Variablen an den Browser weiter, deren Name mit VITE_ beginnt. Das ist kein Namensschema, sondern eine Freigabe. Diese beiden Werte landen im Browser und sind für jeden lesbar. Deshalb steht dort der anon key und niemals der service_role key. Dasselbe Prinzip wie die Zugriffssperre aus Schritt 03: Standardmäßig ist nichts freigegeben.

3 · Neustart des Entwicklungsservers

Nach dem Anlegen der .env muss der Entwicklungsserver neu gestartet werden. Speichern allein reicht nicht, die Werte werden beim Start eingelesen. Das ist die häufigste Fehlerquelle des Abends. Im Terminal beendest du mit Strg und C, auch auf dem Mac. Nicht Cmd. Danach npm run dev.

4 · Die Tabelle, in zwei SQL-Blöcken

Claude hat keinen Zugriff auf deine Datenbank. Er schreibt das SQL, ausführen musst du es selbst im SQL Editor von Supabase.

Für die Blaupause aus Schritt 01, Kontakte, sind beide Blöcke fertig ausformuliert. Bei einem eigenen Anwendungsfall tauschst du nur den Tabellennamen. Die Spalten stehen ausschließlich im ersten Block.

Block 1 · Anlegen und sperren

create table kontakte (
  id uuid primary key default gen_random_uuid(),
  angelegt_am timestamptz not null default now(),
  vorname text not null,
  nachname text not null,
  firma text,
  email text,
  telefon text,
  notiz text
);

alter table kontakte enable row level security;

Das alter table gehört dazu: Tabellen aus dem Table Editor sind automatisch gesperrt, Tabellen aus dem SQL Editor nicht. Wer das vergisst, hat eine offene Tabelle im Netz.

Zwei Beispielzeilen, damit Wegmarke 1 etwas zu zeigen hat:

insert into kontakte (vorname, nachname, firma, email, telefon, notiz) values
  ('Erika', 'Mustermann', 'Mustermann GmbH', '[email protected]', '+49 170 1234567', 'Kennengelernt auf der Messe'),
  ('Max', 'Beispiel', 'Beispiel AG', '[email protected]', '+49 170 7654321', 'Rueckruf vereinbart');

Block 2 · Öffnung

create policy "lesen" on kontakte for select using (true);
create policy "anlegen" on kontakte for insert with check (true);
create policy "aendern" on kontakte for update using (true);
create policy "loeschen" on kontakte for delete using (true);
Achtung bei using (true): Das heißt „jeder darf alles". Für heute ist das richtig, für echte Daten ist es falsch. Sobald deine Anwendung im Netz steht und echte Daten enthält, gehört dort eine Regel hin, die an den angemeldeten Nutzer gebunden ist.

5 · Drei Wegmarken

++ Plus Ladezustand und Leerzustand

Eine leere weiße Fläche ist die schlechteste Antwort, die eine Anwendung geben kann. Wer davor sitzt, weiß nicht, ob sie lädt, leer ist oder kaputt.

Behandle drei Faelle sauber: Waehrend die Daten geladen werden,
zeige einen Ladehinweis. Wenn noch keine Eintraege vorhanden sind,
zeige stattdessen einen kurzen Hinweis mit der Aufforderung, den
ersten Eintrag anzulegen. Und falls die Datenbank einen Fehler
zurueckgibt, zeige eine verstaendliche Meldung statt einer leeren
Seite.

Der dritte Fall zahlt sich aus. Wenn heute Abend etwas klemmt, siehst du damit sofort, woran es liegt.

+++ Kür Bearbeiten und löschen
Mache die Eintraege bearbeitbar: Ein Klick auf einen Eintrag
oeffnet die Felder zum Aendern, Speichern schreibt die Aenderung
in die Datenbank zurueck. Ergaenze ausserdem bei jedem Eintrag
einen Loeschen-Knopf, der den Eintrag nach einer Rueckfrage aus
der Datenbank entfernt.
+++ Kür Supabase MCP

Erst wenn alle drei Wegmarken von Hand erledigt sind. Wer die Tabelle nie selbst im Table Editor gesehen hat, versteht in Schritt 05 nicht, was die zwei Fenster zeigen.

Danach den Supabase MCP in Claude Code verbinden. Claude legt dann Tabellen und Policies selbst an, ohne Wechsel in den Browser. Als Beispiel: eine zweite Tabelle.

Finger weg vom Token im falschen Ort. Für die Anbindung braucht Claude einen Personal Access Token, und der gilt für alle deine Projekte, nicht nur für dieses. Derselbe Grundsatz wie beim service_role key: kein Token ins Repository, kein Token in einen Chat.

Pause

ca. 8 Minuten

Nichts tun. Terminal offen lassen, Rechner nicht zuklappen.

Zurück im Call: Schreib in einem Wort in den Chat, was gerade auf deinem Bildschirm steht.

05

Zwei Fenster, eine Wahrheit

ca. 12 Minuten · alle gemeinsam

Hier gibt es kein Plus und keine Kür. Wir machen das alle gleichzeitig.

Stell zwei Browserfenster nebeneinander. Links deine Anwendung aus Schritt 04 auf localhost, mit Formular und Tabelle. Rechts in Supabase der Table Editor mit deiner Tabelle: Das Raster zeigt die Datensätze, und Insert row öffnet die Eingabefelder.

Leg links über dein Formular einen Eintrag an, lade rechts neu. Die Zeile ist da. Dann umgekehrt: leg rechts über Insert row eine Zeile an, lade links neu. Sie erscheint.

Falls deine Anwendung noch nicht schreiben kann, prüf nur die Leserichtung: rechts eine Zeile anlegen, links neu laden. Der Punkt ist derselbe.

Der Punkt daran, ausgesprochen: Deine Anwendung und deine Daten sind zwei getrennte Dinge. Die Anwendung läuft auf deinem Rechner. Die Daten liegen in Frankfurt. Du könntest die Anwendung morgen löschen und neu bauen, die Daten wären noch da.

Kurz zeigen

Zwei bis drei Teilnehmer zeigen ihre Anwendung. Der Rest schaut zu.

06

Abschluss und Ausblick

ca. 10 Minuten

1 · Das offene Issue

Linear Das zweite Issue aus Schritt 01 auf Done setzen, wenn die Anwendung Einträge erfasst und als Tabelle zeigt.

2 · Echte Zugriffsregeln

Heute steht überall using (true): jeder darf alles. Für echte Daten muss die Regel an den angemeldeten Nutzer gebunden sein.

Die Regel steht in der Datenbank, nicht im Programm. Deshalb kann sie niemand umgehen, indem er die Anwendung verändert.

3 · Backlog füllen

Die Issues von heute und das Backlog sind dieselbe Sorte Eintrag. Das eine ist erledigt, das andere noch nicht. Trag Ergebnisse ein, beginnend mit dem Punkt von eben:

4 · Weg ins Netz

Deine Anwendung läuft bisher nur lokal. Der Weg auf den eigenen VPS ist dir bekannt, das Hello World steht ja schon dort. Bei einer Anwendung mit Datenbank sind zwei Dinge anders:

+++ Kür Linear MCP

Linear MCP in Claude Code verbinden. Danach schreibt Claude die offenen Punkte aus der Liste oben selbst als Issues, statt dass du sie abtippst.

Finger weg vom Token im falschen Ort. Auch hier braucht Claude Zugangsdaten. Derselbe Grundsatz wie beim service_role key und beim Supabase-Token: kein Token ins Repository, kein Token in einen Chat.

Und der nächste Schritt danach, nicht der heutige: Claude arbeitet nicht mehr nur an deinem Code. Mit MCP greift er auch auf deine Datenbank und dein Backlog zu.

Diese Seite bleibt online. Wenn du in der Session nicht mitgekommen bist oder gar nicht dabei warst, arbeite sie in Ruhe durch. Die Schritte funktionieren einzeln, nur der gemeinsame Teil in Schritt 05 braucht die Gruppe.

Und der Satz vom Anfang gilt weiter: Basis reicht. Wer heute eine Anwendung mit eigener Datenbank zum Laufen gebracht hat, kann etwas, das er vorher nicht konnte. Alles Weitere ist Zugabe.