Here is the translation of the blog post from English to German:
Table of Contents
- Einleitung: Das Backend ist jetzt ein Gespräch
- Was ist dieser "Directus-Skill" eigentlich
- Voraussetzungen: Was Sie benötigen, bevor Sie beginnen
- Installation des Skills in Hermes Agent
- Anatomie einer einzelnen Prompt-Backend-Spezifikation
- Beispiel 1: Blog-Backend (Multilingual-Ready)
- Beispiel 2: E-Commerce-Backend in einem Prompt
- Beispiel 3: Einfaches CRM (Kontakte + Unternehmen + Interaktionen)
- Wie es eigentlich funktioniert: Der Skills geheime Sauce
- Produktionsbewährte Muster, die wir entdeckt haben
- 1. Schema und Meta sind obligatorisch
- 2. Verwenden Sie get_expanded() für Beziehungen
- 3. IDs sind Strings in GraphQL, Ganzzahlen in REST
- 4. Junction-Tabelle-Schreibvorgänge gehen über die Junction-Sammlung
- 5. Übersetzungs-Sammlungen benötigen einen zweistufigen Einfügevorgang
- 6. Der POST-Trunkierungsfehler und die PATCH-Fix
- 7. Überprüfen Sie Schreibzugriff vor der Veröffentlichung
- Fehlerbehandlung: Was schiefgeht und wie Sie es beheben
- Erweiterte Muster: Filter, Beziehungen und Enum-Felder
- Die Admin-API: Wenn Sie das Schema programmatisch ändern müssen
- Alles zusammen: Ihre erste Vibe-Code-Sitzung
- Was dies ermöglicht: Schnelles Prototyping im großen Maßstab
Einleitung: Das Backend ist jetzt ein Gespräch
!Vibe-coding workflow diagram: natural language prompt to Directus schema to production API
Hier ist ein frustrierendes Muster, das die meisten Entwickler kennen:
- Sie haben eine brillante App-Idee
- Sie beginnen mit dem Aufbau der Datenbank -
users,posts,orders - Sie bauen die API-Ebene - REST-Endpunkte, GraphQL-Resolver
- Sie verbinden Authentifizierung, Berechtigungen, Validierung
- Drei Tage später kommen Sie endlich zum eigentlichen Feature, das Sie bauen wollten
Diese Reibung ist real. Und im Jahr 2026 ist sie tot.
Was, wenn Sie Schritte 2 bis 4 ganz überspringen könnten? Was, wenn Sie Ihr gesamtes Backend in einfacher Sprache beschreiben, Enter drücken und ein produktionsbereites Schema, API und Admin-Panel erhalten könnten?
Das bedeutet Vibe-Coding eines Backends. Und mit Directus - dem Open-Source-Headless-CMS, das Ihre Datenbank in eine Instant-API verwandelt - und dem nikola66-Directus-Skill (einem universellen GraphQL-Client für KI-Agenten) ist es jetzt möglich, eine gesamte Datenlage in einem Schritt aufzubauen.
Dies ist kein Spielzeug-Demo. Dies ist ein echter Workflow, den wir bei Ainex verwenden, um Projekt-Backends in Minuten und nicht in Tagen aufzubauen. In diesem Guide erfahren Sie:
- Was der Directus-Skill eigentlich unter der Haube macht
- Wie Sie ihn installieren und mit Ihrem Directus-Instanz verbinden
- Die Anatomie einer einzelnen Prompt-Backend-Spezifikation
- Schritt-für-Schritt-Beispiele: Aufbau eines Blogs, eines E-Commerce-Stores und eines CRMs
- Produktionsmuster, Fehlerbehandlung und Gotchas, die wir bereits im Feld getroffen haben
- Wie Sie überprüfen, ob Ihr neu aufgebauter Backend-Server live und abfragbar ist
Lassen Sie uns loslegen.
Was ist dieser "Directus-Skill" eigentlich?
Der Skill bei nikola66/directus-skill ist ein Python-Client, der Directus' GraphQL- und REST-APIs direkt anspricht. Er ist für KI-Agenten wie Hermes konzipiert, aber Sie können ihn auch als eigenständige Bibliothek verwenden.
Schlüsselmerkmale, die Vibe-Coding möglich machen:
- Sammlungs-agnostisch: Es kümmert sich nicht darum, ob Sie mit
Blog_PostsoderEcom_Productsarbeiten. Die gleichen Methoden funktionieren auf jeder Sammlung. - Vollständige CRUD-Oberfläche:
get,insert,update,delete,countund sogarcreate_collectionundcreate_fieldfür Schema-Operationen (nur Admin). - Intelligente Beziehungs-Erweiterung: Verwenden Sie die Punktnotation wie
author.nameoderorder_items.product.titleund der Skill baut die korrekte verschachtelte GraphQL-Auswahlmenge. - Filteroperatoren: Alle Directus-Filter werden unterstützt -
_eq,_contains,_in,_gt,_nullusw. - Fehlerbehandlung: Löst
DirectusErrormit strukturierten Details aus, damit Sie genau wissen, warum etwas fehlgeschlagen ist.
Wenn Sie diesen Skill in einen KI-Agenten laden, geben Sie diesem Agenten die Fähigkeit, ein ganzes Datenbankschema durch Konversation zu bootstrappen.
Voraussetzungen: Was Sie benötigen, bevor Sie beginnen
Bevor Sie ein Backend vibe-codieren können, benötigen Sie drei Dinge:
- Eine Directus-Instanz - entweder selbst gehostet oder Directus Cloud. Wir laufen bei
https://hub.aratech.ae. - Ein API-Token mit entsprechenden Berechtigungen. Für Schema-Erstellung benötigen Sie ein Admin-Token. Erhalten Sie es von Directus → Settings → API-Tokens → Erstellen (Admin-Bereich, alle Berechtigungen).
- Python 3.9+ mit dem Skill installiert:
# Option A: Installieren aus PyPI (wenn veröffentlicht)
pip install directus-skill
## Option B: Klonen und direkt verwenden (aktuell)
git clone https://github.com/nikola66/directus-skill.git
## Kopieren Sie das Paket in Ihr Projekt oder fügen Sie es zu sys.path hinzuStand April 2026 ist der Skill aktiv gepflegt und gegen eine Directus-10+-Instanz mit vollständigem CRUD, Filteroperatoren und Sammlungsverwaltung validiert.
Installation des Skills in Hermes Agent
Wenn Sie Hermes Agent (unser Multi-Agenten-Orchestrierungsplattform) ausführen, ist das Hinzufügen des Skills ein einzelner Befehl:
hermes skills install directusOder um die upstream-GitHub-Version direkt hinzuzufügen:
npx skills add nikola66/directus-skillSobald installiert, wird der directus-Skill automatisch geladen, wenn Sie nach Directus-Operationen fragen. Sie müssen nichts manuell importieren - der Agent kümmert sich darum.
Setzen Sie Ihre Umgebungsvariablen (wir halten diese in ~/.bashrc oder der Gateway-Sitzung):
export DIRECTUS_URL="https://hub.aratech.ae"
export DIRECTUS_API_TOKEN="user_token_here" # CRUD-Operationen
export DIRECTUS_ADMIN_TOKEN="admin_token_here" # Schema-VerwaltungHinweis: Im Hermes-Gateway werden diese automatisch aus der Gateway-Konfiguration injiziert. Wenn Sie eigenständige Skripte ausführen, setzen Sie sie selbst.
Anatomie einer einzelnen Prompt-Backend-Spezifikation
Die Magie passiert, wenn Sie Ihr gesamtes Datenmodell in einem natürlichen Sprachprompt beschreiben. Hier ist das Muster:
Bauen Sie mir ein [TYPE]-Backend mit Directus. Erstellen Sie diese Sammlungen:
1. [Sammlungsname] - [Zweck]
- Feldname (Typ), Feld2 (Typ), ...
- Beziehungen: [Beziehungsdetails]
2. [Sammlungsname] - ...
...
Nachdem alle Sammlungen erstellt wurden:
- Überprüfen Sie durch Aufruf von get_collections() und Ausgeben der Liste
- Legen Sie [N] Beispiel-Datensätze für jede Sammlung an
- Ausgeben des vollständigen Python-SkriptsDer Agent interpretiert dies, übersetzt es in Directus-Admin-API-Aufrufe (POST /collections mit vollständiger Felddefinition), führt es aus und gibt ein funktionierendes Schema zurück.
Der Skill erledigt die harte Arbeit:
- Baut die korrekte Payload-Struktur für Directus' eigenwillige
schema- undmeta-Anforderungen - Ruft
create_collection()pro Sammlung mit der vollständigen Feldliste auf - Legt optional Beispiel-Daten über CRUD-Operationen an
- Überprüft den Erfolg mit
get_collections()
Alles in einer Ausführung, ohne manuelle Intervention.
Beispiel 1: Blog-Backend (Multilingual-Ready)
Lassen Sie uns mit einem Klassiker beginnen - einem Blog, der 5 Sprachen unterstützt. Hier ist der vollständige Prompt, den Sie dem Agenten geben würden:
Bauen Sie mir ein multilinguales Blog-Backend mit Directus. Erstellen Sie diese Sammlungen:
1. Blog_Authors - Autor-Profile
- Name (String), Slug (String, eindeutig), Bio (Text), Avatar (Datei), Social_Twitter (String, nullable)
- Status: Entwurf/Veröffentlicht
- Felder: ID, Erstellt am, Aktualisiert am
2. Blog_Categories - Hierarchische Taxonomie
- Name (String), Slug (String, eindeutig), Beschreibung (Text), Eltern-Kategorie (Viele-zu-Eins → Blog_Categories, nullable)
- Icon (String, Icon-Name), Farbe (String, Hex)
- Felder: ID, Sortierreihenfolge
3. Blog_Tags - Flache Tags
- Name (String), Slug (String, eindeutig), Farbe (String, Hex)
- Felder: ID
4. Blog_Posts - Eltern-Records (Metadaten nur, pro unserem Schema-Design)
- Status (Entwurf/Veröffentlicht), Veröffentlicht am (Datum, nullable)
- Autor (Viele-zu-Eins → Blog_Authors), Kategorie (Viele-zu-Eins → Blog_Categories)
- Hervorgehobenes Bild (Datei), Lesezeit in Minuten (Ganzzahl)
- Ist hervorgehoben (Boolean), Ist Säule (Boolean)
- Felder: ID, Erstellt am, Aktualisiert am
5. Blog_Posts_Translations - pro-Sprach-Inhalt (Kind-Records)
- Blog_Posts_ID (FK-Ganzzahl → Blog_Posts), Sprachcode (String: en/ar/de/es/fr)
- Titel (String), Slug (String), Inhalt (Markdown-Text)
- Exzerpt (Text, max. 200 Zeichen), Schlüssel-Merkmale (JSON-Array, 3-5 Elemente)
- Meta-Titel (String, ≤60), Meta-Beschreibung (String, ≤160)
- Hervorgehobenes Bild-Alt (String)
- CTA-Überschrift (String), CTA-Schaltflächen-Text (String), CTA-Schaltflächen-URL (String)
- Schema-Markup (JSON - Article schema.org)
- Felder: ID
6. Blog_Posts_Tags - Junction für Viele-zu-Viele-Beziehungen Post ↔ Tag
- Blog_Posts_ID (FK → Blog_Posts), Blog_Tags_ID (FK → Blog_Tags)
- Felder: ID
Wichtig:
- Verwenden Sie create_collection() für alle 6 Sammlungen
- Jedes Feld muss schema: {} und meta: {} in der Payload enthalten
- Definieren Sie alle Felder im Voraus (keine inkrementellen Feldhinzufügungen)
- Nachdem alle Sammlungen erstellt wurden, legen Sie 2 Beispiel-Blog-Beiträge an:
* Beitrag 1: "Einführung in die KI-Sicherheit" - Autor=1, Kategorie=33 (KI & LLMs), veröffentlicht
* Beitrag 2: "Verständnis von SOC 2-Konformität" - Autor=1, Kategorie=37 (Konformität), Entwurf
* Für jeden Beitrag erstellen Sie eine EN-Übersetzung (überspringen Sie AR/DE/ES/FR für jetzt)
- Drucken Sie die Überprüfung: Listen Sie alle Sammlungen und ihre Datensatzanzahlen aufWenn Sie diesen Prompt durch einen Agenten mit dem Directus-Skill ausführen:
- Erstellt 6 Sammlungen mit vollständiger Felddefinition in korrekter Reihenfolge (Eltern vor Kindern, keine zirkulären Abhängigkeiten)
- Fügt korrekte Beziehungen hinzu - Viele-zu-Eins von Posts → Autoren/Kategorie, Viele-zu-Viele über Junction-Tabelle
- Respektiert Ihr tatsächliches Schema-Design - Übersetzungs-Records sind separate Kind-Sammlung, verknüpft mit
blog_posts_id - Legt echte Daten an - Zwei Beispiel-Beiträge mit erforderlichen Feldern
- Überprüft - Gibt aus, was erstellt wurde
Was dies tut
Wenn Sie diesen Prompt durch einen Agenten mit dem Directus-Skill ausführen:
- Erstellt 6 Sammlungen mit vollständiger Felddefinition in korrekter Reihenfolge (Eltern vor Kindern, keine zirkulären Abhängigkeiten)
- Fügt korrekte Beziehungen hinzu - Viele-zu-Eins von Posts → Autoren/Kategorie, Viele-zu-Viele über Junction-Tabelle
- Respektiert Ihr tatsächliches Schema-Design - Übersetzungs-Records sind separate Kind-Sammlung, verknüpft mit
blog_posts_id - Legt echte Daten an - Zwei Beispiel-Beiträge mit erforderlichen Feldern
- Überprüft - Gibt aus, was erstellt wurde
Das generierte Code (Was unter der Haube läuft)
Der Agent produziert etwas wie dieses Python-Skript:
from directus.client import DirectusClient
import os
client = DirectusClient(
url=os.getenv('DIRECTUS_URL'),
token=os.getenv('DIRECTUS_ADMIN_TOKEN') # Admin-Token für Schema-Operationen
)
## 1. Blog_Authors
client.create_collection('Blog_Authors', fields=[
{'field': 'id', 'type': 'integer', 'schema': {'is_primary_key': True, 'has_auto_increment': True}, 'meta': {'hidden': True, 'interface': 'numeric', 'readonly': True}},
{'field': 'name', 'type': 'string', 'schema': {}, 'meta': {'interface': 'input'}},
{'field': 'slug', 'type': 'string', 'schema': {'is_unique': True}, 'meta': {'interface': 'input'}},
{'field': 'bio', 'type': 'text', 'schema': {}, 'meta': {'interface': 'textarea'}},
{'field': 'avatar', 'type': 'file', 'schema': {}, 'meta': {'interface': 'file', 'options': {'accepted_files': 'image/*'}}},
{'field': 'social_twitter', 'type': 'string', 'schema': {}, 'meta': {'interface': 'input'}},
{'field': 'status', 'type': 'string', 'schema': {}, 'meta': {'interface': 'select', 'options': {'choices': ['draft', 'published']}}},
{'field': 'created_at', 'type': 'timestamp', 'schema': {}, 'meta': {'interface': 'datetime', 'readonly': True}},
{'field': 'updated_at', 'type': 'timestamp', 'schema': {}, 'meta': {'interface': 'datetime', 'readonly': True}}
])
## 2. Blog_Categories
client.create_collection('Blog_Categories', fields=[
{'field': 'id', 'type': 'integer', 'schema': {'is_primary_key': True, 'has_auto_increment': True}, 'meta': {'hidden': True, 'interface': 'numeric', 'readonly': True}},
{'field': 'name', 'type': 'string', 'schema': {}, 'meta': {'interface': 'input'}},
{'field': 'slug', 'type': 'string', 'schema': {'is_unique': True}, 'meta': {'interface': 'input'}},
{'field': 'description', 'type': 'text', 'schema': {}, 'meta': {'interface': 'textarea'}},
{'field': 'parent_category', 'type': 'integer', 'schema': {}, 'meta': {'interface': 'relation', 'options': {'related_collection': 'Blog_Categories'}}},
{'field': 'icon', 'type': 'string', 'schema': {}, 'meta': {'interface': 'input'}},
{'field': 'color', 'type': 'string', 'schema': {}, 'meta': {'interface': 'color'}},
{'field': 'sort_order', 'type': 'integer', 'schema': {}, 'meta': {'interface': 'numeric'}},
{'field': 'created_at', 'type': 'timestamp', 'schema': {}, 'meta': {'interface': 'datetime', 'readonly': True}},
{'field': 'updated_at', 'type': 'timestamp', 'schema': {}, 'meta': {'interface': 'datetime', 'readonly': True}}
])
## 3. Blog_Tags
client.create_collection('Blog_Tags', fields=[
{'field': 'id', 'type': 'integer', 'schema': {'is_primary_key': True, 'has_auto_increment': True}, 'meta': {'hidden': True, 'interface': 'numeric', 'readonly': True}},
{'field': 'name', 'type': 'string', 'schema': {}, 'meta': {'interface': 'input'}},
{'field': 'slug', 'type': 'string', 'schema': {'is_unique': True}, 'meta': {'interface': 'input'}},
{'field': 'color', 'type': 'string', 'schema': {}, 'meta': {'interface': 'color'}},
{'field': 'created_at', 'type': 'timestamp', 'schema': {}, 'meta': {'interface': 'datetime', 'readonly': True}}
])
## 4. Blog_Posts - Eltern-Records (Metadaten nur, pro unserem Schema-Design)
client.create_collection('Blog_Posts', fields=[
{'field': 'id', 'type': 'integer', 'schema': {'is_primary_key': True, 'has_auto_increment': True}, 'meta': {'hidden': True, 'interface': 'numeric', 'readonly': True}},
{'field': 'status', 'type': 'string', 'schema': {}, 'meta': {'interface': 'select', 'options': {'choices': ['draft', 'published']}}},
{'field': 'published_at', 'type': 'timestamp', 'schema': {}, 'meta': {'interface': 'datetime', 'readonly': False}},
{'field': 'author', 'type': 'integer', 'schema': {}, 'meta': {'interface': 'relation', 'options': {'related_collection': 'Blog_Authors'}}},
{'field': 'category', 'type': 'integer', 'schema': {}, 'meta': {'interface': 'relation', 'options': {'related_collection': 'Blog_Categories'}}},
{'field': 'featured_image', 'type': 'file', 'schema': {}, 'meta': {'interface': 'file', 'options': {'accepted_files': 'image/*'}}},
{'field': 'reading_time_minutes', 'type': 'integer', 'schema': {}, 'meta': {'interface': 'numeric'}},
{'field': 'is_featured', 'type': 'boolean', 'schema': {}, 'meta': {'interface': 'boolean'}},
{'field': 'is_pillar', 'type': 'boolean', 'schema': {}, 'meta': {'interface': 'boolean'}},
{'field': 'created_at', 'type': 'timestamp', 'schema': {}, 'meta': {'interface': 'datetime', 'readonly': True}},
{'field': 'updated_at', 'type': 'timestamp', 'schema': {}, 'meta': {'interface': 'datetime', 'readonly': True}}
])
## 5. Blog_Posts_Translations - pro-Sprach-Inhalt (Kind-Records)
client.create_collection('Blog_Posts_Translations', fields=[
{'field': 'id', 'type': 'integer', 'schema': {'is_primary_key': True, 'has_auto_increment': True}, 'meta': {'hidden': True, 'interface': 'numeric', 'readonly': True}},
{'field': 'blog_posts_id', 'type': 'integer', 'schema': {}, 'meta': {'interface': 'relation', 'options': {'related_collection': 'Blog_Posts'}}},
{'field': 'languages_code', 'type': 'string', 'schema': {}, 'meta': {'interface': 'select', 'options': {'choices': ['en', 'ar', 'de', 'es', 'fr']}}},
{'field': 'title', 'type': 'string', 'schema': {'is_required': True}, 'meta': {'interface': 'input'}},
{'field': 'slug', 'type': 'string', 'schema': {'is_required': True, 'is_unique': True}, 'meta': {'interface': 'input'}},
{'field': 'content', 'type': 'text', 'schema': {}, 'meta': {'interface': 'markdown'}},
{'field': 'excerpt', 'type': 'text', 'schema': {}, 'meta': {'interface': 'textarea'}},
{'field': 'key_takeaways', 'type': 'json', 'schema': {}, 'meta': {'interface': 'list'}},
{'field': 'meta_title', 'type': 'string', 'schema': {}, 'meta': {'interface': 'input'}},
{'field': 'meta_description', 'type': 'text', 'schema': {}, 'meta': {'interface': 'textarea'}},
{'field': 'featured_image_alt', 'type': 'text', 'schema': {}, 'meta': {'interface': 'textarea'}},
{'field': 'cta_headline', 'type': 'string', 'schema': {}, 'meta': {'interface': 'input'}},
{'field': 'cta_button_text', 'type': 'string', 'schema': {}, 'meta': {'interface': 'input'}},
{'field': 'cta_button_url', 'type': 'string', 'schema': {}, 'meta': {'interface': 'input'}},
{'field': 'schema_markup', 'type': 'json', 'schema': {}, 'meta': {'interface': 'json'}},
{'field': 'created_at', 'type': 'timestamp', 'schema': {}, 'meta': {'interface': 'datetime', 'readonly': True}},
{'field': 'updated_at', 'type': 'timestamp', 'schema': {}, 'meta': {'interface': 'datetime', 'readonly': True}}
])
## 6. Blog_Posts_Tags - Junction-Tabelle
client.create_collection('Blog_Posts_Tags', fields=[
{'field': 'id', 'type': 'integer', 'schema': {'is_primary_key': True, 'has_auto_increment': True}, 'meta': {'hidden': True, 'interface': 'numeric', 'readonly': True}},
{'field': 'blog_posts_id', 'type': 'integer', 'schema': {}, 'meta': {'interface': 'relation', 'options': {'related_collection': 'Blog_Posts'}}},
{'field': 'blog_tags_id', 'type': 'integer', 'schema': {}, 'meta': {'interface': 'relation', 'options': {'related_collection': 'Blog_Tags'}}},
{'field': 'created_at', 'type': 'timestamp', 'schema': {}, 'meta': {'interface': 'datetime', 'readonly': True}}
])
## Legen Sie Beispiel-Daten an
## 1. Erstellen Sie einen Autor (wenn nicht vorhanden)
author = client.insert({'name': 'Alex Chen', 'slug': 'alex-chen', 'bio': 'Sicherheitsingenieur bei Ainex.', 'status': 'published'}, collection='Blog_Authors')
author_id = author['id']
## 2. Erstellen Sie eine Kategorie
category = client.insert({'name': 'KI & LLMs', 'slug': 'ki-llms', 'description': 'Artikel über KI-Sicherheit und LLMs.', 'sort_order': 1}, collection='Blog_Categories')
category_id = category['id']
## 3. Erstellen Sie den Eltern-Blog-Beitrag-Record
post = client.insert({
'status': 'published',
'author': author_id,
'category': category_id,
'reading_time_minutes': 8,
'is_featured': False,
'published_at': '2026-05-03T10:00:00.000Z'
}, collection='Blog_Posts')
post_id = post['id']
## 4. Erstellen Sie die englische Übersetzung
client.insert({
'blog_posts_id': post_id,
'languages_code': 'en',
'title': 'Einführung in die KI-Sicherheit',
'slug': 'einfuehrung-in-die-ki-sicherheit',
'content': '# KI-Sicherheitsgrundlagen\n\nDies ist ein Beispielartikel über die Sicherheit von KI-Systemen...\n\n## Schlüsselrisiken\n\n- Prompt-Injektion\n- Datenleakage\n- Modellvergiftung\n\n## Best Practices\n\nImmer Eingaben validieren, Modellausführung sandboxen und Anomalien überwachen.',
'excerpt': 'Ein Anfänger-freundlicher Leitfaden zur Verständnis und Minderung von KI-Systemrisiken.',
'key_takeaways': ['KI-Systeme sind anfällig für Prompt-Injektionsangriffe', 'Immer Eingaben validieren und Modellausführung sandboxen', 'Anomalien überwachen'],
'meta_title': 'Einführung in die KI-Sicherheit - Ainex-Leitfaden',
'meta_description': 'Erfahren Sie die Grundlagen der KI-Sicherheit, gemeinsame Bedrohungen und Best Practices für den Schutz Ihrer LLM-Anwendungen.',
'featured_image_alt': 'Abstrakte Visualisierung von neuronalen Netzwerksicherheit',
'cta_headline': 'Bereit, Ihre KI zu sichern?',
'cta_button_text': 'Loslegen',
'cta_button_url': '/loslegen',
'schema_markup': {
'@context': 'https://schema.org',
'@type': 'Article',
'headline': 'Einführung in die KI-Sicherheit',
'author': {'@type': 'Person', 'name': 'Alex Chen'},
'datePublished': '2026-05-03T10:00:00.000Z',
'description': 'Ein Anfänger-freundlicher Leitfaden zur Verständnis und Minderung von KI-Systemrisiken.'
}
}, collection='Blog_Posts_Translations')
print(f"✅ Blog-Backend erstellt! Beitrag-ID: {post_id}")Dieses Skript wird vom Agenten generiert und ausgeführt. Sie erhalten ein vollständiges, abfragbares Schema und angelegte Daten sofort.
Beispiel 2: E-Commerce-Backend in einem Prompt
Lassen Sie uns größer denken. Hier ist, wie Sie ein vollständiges E-Commerce-Datenmodell aufbauen würden:
Bauen Sie ein Produktions-E-Commerce-Backend mit Directus. Erstellen Sie diese Sammlungen:
1. **Produkte** - Katalogartikel
- Titel, Slug (eindeutig), Beschreibung (Text), Status (Entwurf/Verfügbar/Ausverkauft)
- Preis_Cents (Ganzzahl), Vergleichspreis_Cents (Ganzzahl, nullable), Kosten_Cents (Ganzzahl)
- SKU (eindeutig), Barcode (nullable), Gewicht_Gramm (Ganzzahl)
- Lagerbestand, Niedriger_Bestand_Schwelle (Standard 10)
- Hervorgehobenes Bild (Datei), Bilder (Viele-zu-Viele über Junction)
- Kategorie (Viele-zu-Eins → Kategorien), Tags (Viele-zu-Viele → Tags über Junction)
- Veröffentlicht am
2. **Kategorien** - Hierarchische Taxonomie
- Name, Slug (eindeutig), Beschreibung, Eltern-Kategorie (nullable Viele-zu-Eins self)
- Bild (Datei), Sortierreihenfolge
3. **Tags** - Produkt-Tags
- Name, Slug (eindeutig), Farbe (Hex)
4. **Kunden** - Konten
- E-Mail (eindeutig), Vorname, Nachname, Telefon
- Standard-Versandadresse (JSON), Standard-Rechnungsadresse (JSON)
- Gesamtausgaben_Cents, Bestellanzahl, Letzte_Bestellung_am (nullable)
- Status (aktiv/blockiert), Notizen
5. **Bestellungen** - Transaktionen
- Bestellnummer (eindeutig), Status (ausstehend/verarbeitet/versendet/liefert/cancelled)
- Kunde (Viele-zu-Eins → Kunden)
- Gesamtpreis_Cents, Steuer_Cents, Versandkosten_Cents, Gesamtbetrag_Cents, Rabatt_Cents
- Versandadresse, Rechnungsadresse (JSON)
- Zahlungsstatus (bezahlt/rückerstattet/fehlgeschlagen), Zahlungsmethode (stripe/paypal/manual)
- Zahlungsabsichts-ID (nullable), Tracking-Nummer (nullable)
- Notizen, Storniert am (nullable)
6. **Bestellpositionen** - Zeilenartikel (Junction-Tabelle Bestellungen ↔ Produkte)
- Bestellung (Viele-zu-Eins → Bestellungen), Produkt (Viele-zu-Eins → Produkte)
- Menge, Einzelpreis_Cents, Gesamtpreis_Cents
- Variantenname (String, nullable)
7. **Inventar-Anpassungen** - Prüfungprotokoll
- Produkt (Viele-zu-Eins → Produkte), Grund (nachbestellen/verkaufen/rückgabe/prüfen/anpassen)
- Mengenänderung, vorherige Menge, neue Menge
- Referenz-ID (nullable - verknüpft mit Bestellung/anpassen), Notizen
8. **Sammlungen** - Curated-Gruppierungen
- Name, Slug (eindeutig), Handle (API-freundlicher String)
- Typ (manual/automatisch), Regeln (JSON für automatische Regeln)
- Sortierreihenfolge, Veröffentlicht (Boolean)
9. **Rabatte** - Promo-Codes
- Code (eindeutig, nullable für automatisch), Typ (Prozentsatz/fest)
- Wert (Ganzzahl - Prozentsatz oder Cents), Maximalverwendungen, Verwendungsanzahl
- Gültig ab, Gültig bis (nullable)
- Gilt für (alle/kategorien/produkte), anwendbare Produkt-IDs (JSON-Array)
- Mindestbestellwert_Cents (nullable), Ausschluss von Verkaufsartikeln (Boolean)
Einschränkungen:
- Verwenden Sie create_collection() für alle 9 Sammlungen + explizite Junction-Tabellen (Produkte_Bilder wenn erforderlich)
- Jedes Feld: enthalten Sie schema: {} und meta: {} im Voraus
- Verwenden Sie richtige Directus-Feldtypen: Ganzzahl, String, Text, Boolean, Datum, JSON, Datei, Pivot
- Fügen Sie eindeutige Indizes auf Slug, SKU, Bestellnummer hinzu
- Legen Sie 3 Beispiel-Produkte mit Beispiel-Bildern an (verwenden Sie Datei-IDs wenn verfügbar), verknüpfen Sie mit Kategorie
- Überprüfen: Aufruf von get_collections() und Ausgeben der Datensatzanzahlen
- Ausgeben des vollständigen Python-SkriptsDas Ausführen dieses Prompts durch einen Agenten produziert ein ~300-zeiliges Skript, das Ihr gesamtes E-Commerce-Datenmodell in einem Schritt aufbaut. Von Null auf "Hier ist Ihr Admin-Panel" bevor Ihr Kaffee kalt wird.
Beispiel 3: Einfaches CRM (Kontakte + Unternehmen + Interaktionen)
Benötigen Sie etwas Leichtgewichtiges? Hier ist ein CRM:
Bauen Sie ein CRM-Backend mit Directus:
1. Unternehmen
- Name, Domäne (eindeutig), Branche, Größe (1-10/11-50/51-200/200+)
- Website, Telefon, Adresse (JSON), Jahresumsatz_Cents (nullable)
- Besitzer (Viele-zu-Eins → Benutzer), Status (Lead/Kunde/Interessent/Abgelehnt)
- Letzter Kontakt am, Notizen (Text)
2. Kontakte
- Vorname, Nachname, E-Mail (eindeutig), Telefon, Job-Titel
- Unternehmen (Viele-zu-Eins → Unternehmen), Besitzer (Viele-zu-Eins → Benutzer)
- Status (aktiv/inaktiv/Lead), bevorzugte Kontaktmethode (E-Mail/Telefon/LinkedIn)
- LinkedIn-URL, Notizen
3. Interaktionen
- Kontakt (Viele-zu-Eins → Kontakte), Typ (Anruf/E-Mail/Treffen/Notiz)
- Richtung (eingehend/ausgehend), Dauer in Minuten (Ganzzahl)
- Zusammenfassung (Text), nächste Schritte (Text), Fälligkeitsdatum (nullable)
- Verwandte Geschäftschance (Viele-zu-Eins → Geschäftschancen nullable)
4. Geschäftschancen
- Titel, Wert_Cents, Phase (Vorschlag/Verhandlung/abgeschlossen/verloren)
- Wahrscheinlichkeit (0-100), Abschlussdatum (erwartet)
- Kontakt (Viele-zu-Eins → Kontakte), Besitzer (Viele-zu-Eins → Benutzer)
- Notizen
Einschränkungen:
- Verwenden Sie create_collection() für alle 4
- Alle Standardfelder: ID, Erstellt am, Aktualisiert am werden automatisch hinzugefügt
- Beziehungen werden korrekt eingerichtet (Fremdschlüssel sind Ganzzahlen)
- Legen Sie 2 Unternehmen, 4 Kontakte, 3 Interaktionen, 1 Geschäftschance anWiederum - ein Prompt, eine Ausführung.
Wie es eigentlich funktioniert: Der Skills geheime Sauce
Lassen Sie uns entmystifizieren, was passiert, wenn Sie den Agenten bitten, "ein Backend zu bauen":
Schritt 1 - Agent parse Ihr Prompt
Der Agent verwendet seine Argumentation, um zu extrahieren:
- Sammlungsnamen und ihren Zweck
- Feldnamen, -typen und -einschränkungen
- Beziehungsrichtungen (Viele-zu-Eins, Viele-zu-Viele)
- Anforderungen an Beispiel-Daten
- Überprüfungsschritte
Er muss kein Schema-Definitionssprache sehen - er liest einfach Ihre einfache Sprache und baut die Struktur.
Schritt 2 - Skill generiert korrekte Payloads
Dies ist der Teil, in dem der Skill glänzt. Directus' POST /collections-Endpunkt ist berüchtigt picky:
Ohne schema: {} und meta: {} pro Feld? → 403 Forbidden (auch mit einem gültigen Admin-Token).
Falscher Feldtypname? → Die Sammlung erscheint nicht.
Fehlende Beziehungs-Meta-Optionen? → Beziehungen funktionieren nicht.
Der Skill kennt den Directus-API-Vertrag und generiert produktionskorrekte Payloads automatisch. Hier ist, wie eine korrekte Felddefinition aussieht:
{
'field': 'price_cents',
'type': 'integer',
'schema': {'is_required': True}, # ← immer enthalten
'meta': {'interface': 'numeric'} # ← immer enthalten
}Schritt 3 - Admin-API-Aufrufe in Reihenfolge
Sammlungen werden in Abhängigkeitsreihenfolge erstellt (Eltern vor Kindern, keine zirkulären Abhängigkeiten). Für jede:
POST /collectionserstellt die Sammlung mit vollständiger Feldliste- Skill überprüft mit
GET /collections/{name}, dass sie existiert - Wenn sie bereits existiert, wird sie gelöscht und neu erstellt (idempotenter Modus)
Für Junction-Tabellen (Viele-zu-Viele) wird der pivot-Typ oder explizite Fremdschlüssel-Felder mit meta.interface: 'relation' verwendet.
Schritt 4 - Beispiel-Daten + Überprüfung
Nachdem alle Sammlungen existieren, führt der Agent aus:
- Fügt Beispiel-Datensätze mit Standard-CRUD (
insert()) hinzu - Fragt zurück mit
get()oderget_expanded()zur Bestätigung - Gibt eine Zusammenfassung auf Ihrem Terminal aus
Produktionsbewährte Muster, die wir entdeckt haben
Wir haben diesen Workflow gegen https://hub.aratech.ae mit unserem vollständigen Blog-Schema getestet. Hier sind die Lektionen:
1. Schema und Meta sind obligatorisch
Dies kann nicht genug betont werden. Directus 10+ gibt 403 zurück, wenn eines davon fehlt, aber die Fehlermeldung ist irreführend ("Sie haben keine Berechtigung") wenn das eigentliche Problem die Payload-Struktur ist.
Überprüfen Sie immer:
- Sammlungspayload hat
schema: {}undmeta: {}auf oberster Ebene - Jedes einzelne Feld hat sowohl
schemaals auchmeta - Feldtypwerte sind gültige Directus-Typen (
integer, nichtint;string, nichtvarchar)
2. Verwenden Sie get_expanded() für Beziehungen
Directus GraphQL erfordert verschachtelte Auswahlmengen für Beziehungs-Felder. Wenn Sie versuchen:
{ Blog_Posts_Translations { id blog_posts_id } }Sie erhalten einen 400-Fehler: Field "blog_posts_id" of type "Blog_Posts" must have a selection of subfields.
Der Skills get_expanded()-Methode mit Punktnotation handhabt dies automatisch:
client.get_expanded(
fields=['id', 'title', 'blog_posts_id.id', 'blog_posts_id.status'],
collection='Blog_Posts_Translations',
limit=50
)
## Gibt Dictionaries zurück: blog_posts_id = {'id': '5', 'status': 'published'}3. IDs sind Strings in GraphQL, Ganzzahlen in REST
GraphQL gibt alle id-Felder als Strings zurück. Aber Fremdschlüssel-Felder (wie blog_posts_id) sind Ganzzahlen, wenn sie als Filter oder Einfügewerte verwendet werden.
Wenn Sie einen Beitrag erstellen und post_id = "42" zurückbekommen, müssen Sie umwandeln:
client.insert({
'blog_posts_id': int(post_id), # NICHT post_id
'languages_code': 'en',
...
}, collection='Blog_Posts_Translations')Andernfalls erhalten Sie einen 400-GraphQL-Validierungsfehler.
4. Junction-Tabelle-Schreibvorgänge gehen über die Junction-Sammlung
Directus stellt Viele-zu-Viele-Beziehungen als schreibgeschützte virtuelle Felder auf den Eltern dar. Sie können nicht das tags-Feld auf Blog_Posts direkt PATCHEN - Sie erhalten 403 Forbidden.
Stattdessen schreiben Sie in die Junction-Tabelle:
## FALSCH: Direktes PATCHEN auf Blog_Posts schlägt fehl
client.update(post_id, {'tags': [1, 2, 3]}, collection='Blog_Posts') # ❌ 403
## RICHTIG: Einfügen in die Junction-Tabelle
for tag_id in [1, 2, 3]:
client.insert({'blog_posts_id': post_id, 'blog_tags_id': tag_id}, collection='Blog_Posts_Tags')5. Übersetzungs-Sammlungen benötigen einen zweistufigen Einfügevorgang
Pro unserem Schema-Design: Erstellen Sie zuerst den Eltern-Blog_Posts-Record, dann den Kind-Blog_Posts_Translations-Record mit dem Fremdschlüssel:
## Schritt 1: Eltern (nur Metadaten)
post = client.insert({
'status': 'published',
'author': author_id,
'category': category_id,
'reading_time_minutes': 8
}, collection='Blog_Posts')
post_id = post['id']
## Schritt 2: Übersetzung (tatsächlicher Inhalt)
client.insert({
'blog_posts_id': post_id,
'languages_code': 'en',
'title': 'Mein Beitrag',
'slug': 'mein-beitrag',
'content': '# Hallo Welt...',
...
}, collection='Blog_Posts_Translations')6. Der POST-Trunkierungsfehler und die PATCH-Fix
Wir haben herausgefunden, dass große content-Feld-POSTs (>~1500 Zeichen) durch eine Gateway/Proxy-Ebene stillschweigend getrunkiert werden. Die REST-Antwort gibt Erfolg zurück, aber nur die ersten ~1,5 KB Ihres Markdowns werden gespeichert.
Abhilfe: Erstellen Sie den Übersetzungs-Record mit anfänglichen Daten, dann patchen Sie das content-Feld:
## Schritt A: Erstellen (Trunkierungsrisiko)
trans = client.insert({ ... anfängliche Felder ..., 'content': 'kurzer Platzhalter' }, collection='Blog_Posts_Translations')
trans_id = trans['id']
## Schritt B: PATCHEN des vollständigen Inhalts (umgeht Trunkierung)
client.update(trans_id, {'content': vollständiger_markdown_inhalt}, collection='Blog_Posts_Translations')7. Überprüfen Sie Schreibzugriff vor der Veröffentlichung
Bevor Sie einen kritischen Schreibvorgang (Veröffentlichen, Massenaktualisierung) ausführen, führen Sie einen leichten Test durch:
## Senden Sie einen minimalen Test-Record
test = {'status': 'draft', 'author': 1, 'category': 33}
test_record = client.insert(test, collection='Blog_Posts')
test_id = test_record['id']
## Wenn erfolgreich, löschen Sie es
client.delete(test_id, hard=True, collection='Blog_Posts')
print('✅ Schreibzugriff bestätigt')Wenn dies fehlschlägt mit 403/404, hat Ihr Token keine Schreibberechtigungen - kein Punkt, den vollen Vorgang zu versuchen.
Fehlerbehandlung: Was schiefgeht und wie Sie es beheben
Hier ist die Fehler-Tabelle aus der Produktion:
Der Skill behandelt die meisten davon bereits intern (v1.4.1+), aber es ist gut, die Muster zu kennen.
Erweiterte Muster: Filter, Beziehungen und Enum-Felder
Filtern mit Operatoren
Der Skill unterstützt alle Directus-Filteroperatoren:
## Gleich
ergebnisse = client.get(
fields=['id', 'title', 'status'],
filters={'status': {'_eq': 'published'}},
collection='Blog_Posts',
limit=10
)
## Ungleich
entwürfe = client.get(filters={'status': {'_neq': 'published'}}, collection='Blog_Posts')
## Enthält (case-insensitive)
suche = client.get(
filters={'title': {'_icontains': 'sicherheit'}},
collection='Blog_Posts'
)
## In Liste
ergebnisse = client.get(
filters={'category': {'_in': [33, 37, 42]}},
collection='Blog_Posts'
)
## Größer als
teuer = client.get(
filters={'price_cents': {'_gt': 10000}},
collection='Products'
)
## Null-Prüfung
nicht_kontaktiert = client.get(
filters={'last_contacted_at': {'_null': True}},
collection='Contacts'
)
## Kombiniert (ANDed zusammen)
ergebnisse = client.get(
filters={
'status': {'_eq': 'published'},
'category': {'_in': [33, 37]},
'published_at': {'_not_null': True}
},
collection='Blog_Posts'
)Mehrstufige Beziehungs-Erweiterung
Mit Punktnotation können Sie verschachtelte Beziehungen in einem Aufruf abrufen:
## Abrufen von Beiträgen mit Autor, Kategorie und Tags
records = client.get_expanded(
fields=[
'id', 'title', 'status',
'author.id', 'author.name', 'author.email', # Viele-zu-Eins-Beziehung
'category.id', 'category.name', # Viele-zu-Eins-Beziehung
'tags.id', 'tags.name', 'tags.slug' # Viele-zu-Viele über Junction
],
collection='Blog_Posts',
limit=20
)
## Ergebnis: Jeder Record hat verschachtelte Dictionaries wie {'author': {'id': 1, 'name': 'Alex'}, 'tags': [{'id': 5, 'name': 'KI'}, ...]}JSON-Felder für flexible Daten
Verwenden Sie den json-Typ für beliebige strukturierte Daten:
client.insert({
'shipping_address': {
'straße': '123 Tech Blvd',
'stadt': 'Dubai',
'land': 'VAE',
'postleitzahl': '00000'
},
'metadata': {
'utm_source': 'newsletter',
'campaign_id': 'Q2-2026-ki-launch'
}
}, collection='Orders')Das JSON wird native gespeichert und ist abfragbar mit Filtern auf verschachtelten Schlüsseln.
Die Admin-API: Wenn Sie das Schema programmatisch ändern müssen
Der Skill expose create_collection() und create_field() über die Directus Admin-REST-API (/collections und /fields/{collection}), nicht GraphQL.
Admin-Token erforderlich. Ihr DIRECTUS_API_TOKEN ist für CRUD; DIRECTUS_ADMIN_TOKEN ist für Schema-Operationen.
client = DirectusClient(
url=os.getenv('DIRECTUS_URL'),
token=os.getenv('DIRECTUS_API_TOKEN'), # für CRUD
admin_token=os.getenv('DIRECTUS_ADMIN_TOKEN') # für Schema
)
## Erstellen einer neuen Sammlung
client.create_collection('TEST_Items', fields=[
{'field': 'id', 'type': 'integer', 'schema': {'is_primary_key': True, 'has_auto_increment': True}, 'meta': {'hidden': True, 'interface': 'numeric', 'readonly': True}},
{'field': 'name', 'type': 'string', 'schema': {}, 'meta': {'interface': 'input'}},
{'field': 'value', 'type': 'integer', 'schema': {}, 'meta': {'interface': 'numeric'}}
])
## Hinzufügen eines Felds zu einer bestehenden Sammlung (mit Vorsicht - kann GraphQL-Schema korrupt machen)
client.create_field('TEST_Items', 'description', 'text', meta={'interface': 'textarea'})
## Auflisten aller Sammlungen
sammlungen = client.get_collections()
print(sammlungen) # ['Blog_Posts', 'Blog_Authors', ...]
## Abrufen des Sammlungsschemas
schema = client.get_collection_schema('Blog_Posts')
print(schema['fields'].keys()) # dict_keys(['id', 'status', 'author', ...])Warnung: Inkrementelles Hinzufügen von Feldern über create_field() kann das GraphQL-Schema für diese Sammlung korrupt machen, was dazu führt, dass alle nachfolgenden GraphQL-Abfragen mit 400 fehlschlagen. Das sicherste Muster: Definieren Sie alle Felder im Voraus in create_collection().
Alles zusammen: Ihre erste Vibe-Code-Sitzung
Hier ist, wie ein vollständiger Workflow aussieht:
## 1. Starten Sie Ihr Terminal (mit Directus-Tokens in der Umgebung)
$ export DIRECTUS_URL="https://hub.aratech.ae"
$ export DIRECTUS_API_TOKEN="sZk-st..." # Benutzer-Token
$ export DIRECTUS_ADMIN_TOKEN="ad-..." # Admin-Token
## 2. Starten Sie den Hermes-Agenten
$ hermes
## 3. Geben Sie den Prompt (Kopieren und Einfügen von oben)
Sie: Bauen Sie mir ein Blog-Backend mit Directus. Erstellen Sie diese Sammlungen: ...
## 4. Agent denkt nach, generiert Skript, führt aus
🤖 Agent: Erstellen von Blog_Authors... ✅
Erstellen von Blog_Categories... ✅
Erstellen von Blog_Tags... ✅
Erstellen von Blog_Posts... ✅
Erstellen von Blog_Posts_Translations... ✅
Erstellen von Blog_Posts_Tags... ✅
Anlegen von 2 Beispiel-Beiträgen... ✅
Überprüfung:
- Blog_Posts: 2 Records
- Blog_Posts_Translations: 2 Records (en nur)
- Blog_Authors: 1 Record
- Blog_Categories: 1 Record
- Blog_Tags: 0 Records
✅ Backend aufgebaut! Ihre Directus-Admin-Oberfläche bei https://hub.aratech.ae/admin zeigt jetzt alle Sammlungen mit Inhalt.
## 5. Sie sind fertig. Überprüfen Sie die Admin-Oberfläche oder fragen Sie:
$ python3 -c "from directus.client import DirectusClient; c = DirectusClient(); print(c.get(collection='Blog_Posts', limit=3))"Das ist es. Von leerer Directus-Instanz zu einem vollständigen Blog-Backend mit Beziehungen und Beispiel-Daten in unter einer Minute.
Was dies ermöglicht: Schnelles Prototyping im großen Maßstab
Dies ist nicht nur darum, Zeit bei der Schemadefinition zu sparen (obwohl das huge ist). Es geht darum, den Feedback-Loop zwischen Idee und funktionierendem Prototyp zu reduzieren:
- Wochenlange Schemadefinition → 5 Minuten, um zu beschreiben, was Sie wollen
- Manuelle Migrationen → Ein generiertes, ausgeführtes Skript
- API-Spezifikationen → Directus generiert automatisch REST- und GraphQL-Endpunkte
- Admin-Oberflächen-Konfiguration → Directus-Admin-Oberfläche ist sofort verfügbar mit Ihren Sammlungen, Feldern und Berechtigungen
Sie können jetzt ein projektspezifisches Backend für:
- Interne Tools (Inventar, Ticketing, Asset-Verfolgung)
- Kundenportale (Bestellverlauf, Support-Tickets)
- SaaS-Datenmodelle (Abonnements, Mandanten, Nutzungsprotokolle)
- MVPs für Kundenarbeit (beschreiben Sie das Domänenmodell, erhalten Sie ein instantes CMS-gestütz