KI BMS
GraphQL-API
Optional: dieselben Daten per GraphQL über einen einzelnen Endpunkt lesen und ändern.
GraphQL ist optional. Die HTTP-API und der WebSocket-Kanal decken bereits jede Operation ab - diese Seite richtet sich an Services, die einen einzigen Endpunkt mit typisierter Abfragesprache bevorzugen. Sende eine Query per POST an /xapi2/graphql mit demselben Bearer-Token wie an den Rest der API und du bekommst dieselben Daten zurück, nur in einem GraphQL-Antwort-Format.
Hinter der Leitung läuft dieselbe Infrastruktur wie bei HTTP und WebSocket - der GraphQL-Endpunkt ist eine dünne Schicht obendrauf, kein paralleles System. Authentifizierung, PAT-Berechtigungsumfang, Anfragelimits pro IP und pro Token, Zeilen-Zugriffsprüfungen, Audit-Logging, Validierungsregeln, Plan-Kontingente und die Live-Verteilung an verbundene WebSocket-Clients laufen über *exakt dieselben Codepfade* wie die REST-Endpunkte. Ein Fix oder eine neue Regel greift in allen drei gleichzeitig.
Das Schema selbst ist bewusst kompakt: ein einziger Object-Typ, dessen Inhalt als JSON-Skalar reist, plus sechs Wurzelfelder (object, objects, createObject, updateObject, deleteObject). Felder pro Modell stehen auf der jeweiligen Modell-Seite - sie bleibt die verbindliche Quelle dafür, welche Daten eine Zeile trägt.
So funktioniert's
Lesen
object(type, id) liefert eine Zeile, objects(type, limit, offset, sort, q, filters, withCount) eine paginierte Liste. Beide respektieren dieselben Zeilen-Zugriffsregeln wie die HTTP-GET-Endpunkte - was du per REST lesen kannst, kannst du hier lesen, und was per REST nicht sichtbar ist, ist hier ebenso unsichtbar.
Schreiben
createObject(type, input), updateObject(type, id, input) und deleteObject(type, id) spiegeln die HTTP-POST/PATCH/DELETE-Endpunkte. Jeder Schreibvorgang läuft durch dieselben Validierungsregeln und Planprüfungen, verteilt sich live an dieselben WebSocket-Abonnenten und landet im selben Audit-Log wie die REST-Endpunkte.
Gleiche Sicherheitsschicht
Authentifizierung, Berechtigungen, Missbrauchs-schutz und Anfragelimits verhalten sich identisch zur HTTP-API. Es gibt keinen "GraphQL-Bypass": jedes Wurzelfeld verbraucht ein eigenes Anfragelimit-Token und durchläuft dieselben Zugriffsprüfungen wie ein REST-Aufruf - ein Dokument mit mehreren Operationen kostet exakt so viel wie die entsprechende Folge von REST-Aufrufen.
Dokumentgrenzen
Ein Dokument ist auf 64 KiB, Tiefe 8, 200 Feld-Selektionen, 10 Wurzelfelder pro Operation und 50 Variablen begrenzt. Diese Grenzen halten die Worst-Case-Kosten einer Query vorhersehbar - bequem oberhalb jeder realistischen Dashboard-Komposition.
Sicherheit
Der GraphQL-Endpunkt nutzt dieselbe Infrastruktur wie die HTTP-API und der WebSocket-Kanal - er ist eine dünne Schicht obendrauf, kein paralleles System. Authentifizierung, PAT-Berechtigungen, Anfragelimits, Zeilen-Zugriffsprüfungen, Audit-Log und Live-Verteilung an WebSocket-Clients laufen über exakt dieselben Codepfade. Was per REST nicht erlaubt ist, ist per GraphQL ebenso nicht erlaubt - eine "GraphQL-Hintertür" gibt es nicht. Genaue Regeln pro Modell siehe die jeweilige Modell-Seite.
Schema
Das Schema ist klein gehalten - eine Object-Form mit JSON-Inhalt plus sechs Wurzelfeldern. Felder pro Modell stehen auf der entsprechenden Modell-Seite.
scalar JSONtype Object {id: ID!type: String!status: StringownedBy: IDcreatedBy: IDupdatedBy: IDappSlug: Stringdata: JSONcreatedAt: StringupdatedAt: StringisArchived: Boolean}type ObjectList {data: [Object!]!count: Int!hasMore: Boolean!limit: Intoffset: Intsort: StringappSlug: Stringtotal: Int}type DeleteResult { ok: Boolean!, id: ID! }type Query {object (type: String!, id: ID!): Objectobjects (type: String!,limit: Int, offset: Int, after: ID,sort: String, q: String,withCount: Boolean, filters: JSON,): ObjectList!}type Mutation {createObject (type: String!, input: JSON!): Object!updateObject (type: String!, id: ID!, input: JSON!): Object!deleteObject (type: String!, id: ID!): DeleteResult!}
Beispiele
curl -X POST https://www.ki-bewerber-management.de/xapi2/graphql \-H "Authorization: Bearer pat_…" \-H "Content-Type: application/json" \-d '{"query": "query($t:String!,$id:ID!){ object(type:$t,id:$id){ id data } }","variables": { "t": "application", "id": "<object-id>" }}'
Grenzen
Ein einzelnes Dokument ist auf folgende Werte begrenzt - bequem oberhalb jeder realistischen Dashboard-Komposition. Diese Grenzen halten die Worst-Case-Kosten einer Query vorhersehbar.
| Max. Query-Größe | 64 KiB |
| Max. Tiefe | 8 |
| Max. Selektionen gesamt | 200 |
| Max. Wurzelfelder pro Op | 10 |
| Max. Variablen | 50 |
| Anfragelimit pro Wurzelfeld | 1 Token (gleicher Pool wie REST) |
Bewusst nicht unterstützt
Subscriptions
Live-Aktualisierungen laufen über den WebSocket-Kanal, der dieselbe Zugriffsschicht durchsetzt. Eine zweite Live-Oberfläche per GraphQL würde nur die Sicherheits-Oberfläche verdoppeln, ohne neue Funktionalität zu bringen.
Fragmente
Mit dem kompakten Schema (eine `Object`-Form, sechs Wurzelfelder) bringen Fragmente keine Wiederverwendung, dafür aber zusätzliche Parser- und Validierungs-Logik. Inline-Selektionen sind ausreichend und einfacher zu prüfen.
Direktiven
Konditionale Selektionen (`@skip` / `@include`) lassen sich auf Client-Seite mit einer Bedingung in der Query-Erzeugung einfacher und sicherer abbilden. Wir akzeptieren keine serverseitige Direktiven-Logik, damit Selektionen auf den ersten Blick lesbar bleiben.
Introspection
Das vollständige Schema steht oben auf dieser Seite und auf den jeweiligen Modell-Seiten - ein zusätzlicher Server-Endpunkt, der das Schema preisgibt, ist nicht nötig und wäre eine Aufklärungs-Quelle für Angreifer.