RATIOserver API – CRUD-Handbuch
Referenz für Aufruf und Auswertung der Standard-CRUD-Endpunkte (get / getFiltered / getById / getKey / insert / update / delete)
1. Grundprinzip
Alle CRUD-Endpunkte folgen demselben Muster, unabhängig von Modul oder Entität:
| Punkt | Regel |
|---|---|
| HTTP-Methode | Immer POST – auch bei den get*-Endpunkten. Parameter stehen im Body, nicht im Query-String. |
| Content-Type | Request und Response immer application/json |
| Body | Immer ein JSON-Objekt, auch wenn keine Parameter nötig sind: {} |
| Auth | Header Authorization: Bearer <jwttoken>, außer bei Routen mit Auth=false |
| URL-Schema | {{baseURL}}/<modul>/<methode>, z. B. http://localhost/ibapi/dokumente/getvorlagen |
<modul> ist der Controller-Bereich (z. B. dokumente, toupac, adressen, anmiet, fuhrpark, incoming), <methode> der Endpunktname klein geschrieben.
2. Die 7 Standard-Endpunkttypen
Pro Entität <Entity> (Tabellenname) existieren i. d. R. diese sieben Endpunkte:
| Endpunkt | Zweck | Beispiel-Body |
|---|---|---|
get<entity> | Liste aller Datensätze | {"fields":["nr","name"],"orderby":"name"} |
get<entity>filtered | Liste, gefiltert nach optionalen Parametern (Abschnitt 4) | {"fields":"*","art":"BRIEF","orderby":"name"} |
get<entity>byid | Einzelner Datensatz über Primärschlüssel | {"nr":1,"fields":"*"} |
get<entity>key | Nächsten freien Schlüsselwert (Generator) ermitteln | {} |
insert<entity> | Neuen Datensatz anlegen | {"name":"...","art":"BRIEF"} |
update<entity> | Bestehenden Datensatz ändern | {"nr":1,"name":"Neuer Wert"} |
delete<entity> | Datensatz löschen | {"nr":1} |
Zusätzliche Steuerparameter (alle lesenden Endpunkte)
fields– siehe Abschnitt 3orderby– Sortierfeld, muss ein erlaubtes Feld sein, sonstSortierfeld "..." ist nicht erlaubt.limit/offset– Pagination, nur beiget<entity>und...filtered(Abschnitt 8). Ohnelimit(bzw.limit:0) werden alle Datensätze geliefert.
3. Feldliste (fields)
Jeder lesende Endpunkt hat eine im Server-Code fest hinterlegte Whitelist erlaubter Felder
(ersichtlich aus Postman-Collection bzw. Quellcode des Endpunkts). fields steuert,
welche dieser Felder in der Antwort landen – nicht, wonach gefiltert wird.
Wert von fields | Bedeutung |
|---|---|
"*" | Alle für diesen Endpunkt erlaubten Felder |
["feld1","feld2"] | Nur die genannten Felder – jedes muss in der Whitelist stehen |
| Schlüssel fehlt ganz | Verhalten wie "*" |
[] | Fehler Keine Felder angegeben. |
| Feldname nicht in Whitelist | Fehler Feld "<name>" ist nicht erlaubt. |
weder "*" noch Array | Fehler "fields" muss "*" oder ein JSON-Array sein. |
Alle Fehler dieser Kategorie kommen mit HTTP 200 – dazu mehr in Abschnitt 6.
4. Filterparameter bei get<entity>filtered
Liefert dieselbe Grundliste wie get<entity>, zusätzlich beliebig viele
Filterbedingungen möglich. Auch hier gilt eine endpunktspezifische Whitelist möglicher Filterfelder.
- Ein Filterfeld wirkt nur, wenn es im Body vorhanden und nicht
nullist. - Fehlt ein Filterfeld, entfällt die Einschränkung ersatzlos – kein Fehler.
- Mehrere Filter werden immer mit AND verknüpft, kein OR über den Standardmechanismus.
- Kein Filter gesetzt ⇒ Verhalten identisch zu
get<entity>. - Jeder Filter prüft auf Gleichheit (
feld = wert), keine Bereichs-/Vergleichsoperatoren, sofern nicht anders dokumentiert. - Ein Feld im Body, das nicht zu den definierten Filterfeldern gehört, wird stillschweigend ignoriert.
// Kein Filter -> alle Datensätze
{ "fields": "*" }
// Ein Filter -> art = "BRIEF"
{ "fields": "*", "art": "BRIEF" }
// Zwei Filter (UND) -> art = "BRIEF" UND bereich = "EINGANG"
{ "fields": "*", "art": "BRIEF", "bereich": "EINGANG" }
DoSelectFilteredDynamic-Endpunkte
Die meisten ...filtered-Endpunkte sind serverseitig über
DoSelectFilteredDynamic implementiert. Filterfelder und SQL-Bedingung sind als zwei
parallele Listen im Server-Code hinterlegt:
CONDITIONS: array[0..1] of string = ('Field1 = :Field1', 'Field2 = :Field2');
FILTER_PARAMS: array[0..1] of string = ('Field1', 'Field2');
- Prüfreihenfolge: nach der im Code festgelegten Reihenfolge (
FILTER_PARAMS[0],[1], ...), nicht nach der Reihenfolge im JSON-Body. - Verkettung: jeder vorhandene, nicht-
nullParameter wird als(CONDITIONS[i])angehängt, alle Bedingungen zusammen perAND:WHERE (Bedingung_A) AND (Bedingung_B) ... - Fehlender Parameter: Bedingung entfällt komplett aus der WHERE-Klausel (nicht
IS NULL, taucht im generierten SQL gar nicht auf). - Kein Parameter gesetzt: WHERE-Klausel leer, Verhalten wie
get<entity>.
{"art":"BRIEF","bereich":"EINGANG"} und {"bereich":"EINGANG","art":"BRIEF"}
erzeugen exakt dieselbe WHERE-Klausel (art = :art) AND (bereich = :bereich), da die
Verkettungsreihenfolge durch FILTER_PARAMS/CONDITIONS im Server-Code
vorgegeben ist – nicht durch den Request.
5. Antwort im Erfolgsfall
a) Leseoperationen ohne Pagination
get<entity>, ...filtered, ...byid, ...key – jeweils ohne limit. HTTP 200
{
"header": {
"nr": "ftinteger",
"name": "ftstring 100",
"erstelltam": "ftdate",
"menge": "ftfloat"
},
"data": [
{ "nr": 1, "name": "Beispiel", "erstelltam": "2026-07-01", "menge": 3.5 },
{ "nr": 2, "name": "Zweiter Eintrag", "erstelltam": null, "menge": 0 }
]
}
header: Feldname → Datentyp (ftstring N,ftinteger,ftfloat,ftdate,ftdatetime,ftblob).data: Array der Datensätze, Feldnamen klein geschrieben;NULLals JSONnull.- Datumsfelder als
"yyyy-mm-dd", Zeitstempel als"yyyy-mm-dd hh:nn:ss". - BLOB-Felder als Base64-String.
- Kein Treffer (z. B. Filter passt auf nichts) ⇒ kein Fehler, sondern
"data": [].
b) Leseoperationen mit Pagination
get<entity> / ...filtered mit limit > 0. HTTP 200
{
"total": 137,
"limit": 20,
"offset": 0,
"data": {
"header": { "nr": "ftinteger", "name": "ftstring 100" },
"data": [ { "nr": 1, "name": "Beispiel" } ]
}
}
header/data liegen hier unter data.header/data.data
– nicht direkt auf oberster Ebene wie bei (a). total ist die Trefferzahl unter
Berücksichtigung aktiver Filter, nicht die Gesamtzahl der Tabelle.
c) Schreiboperationen
insert<entity>, update<entity>, delete<entity>. HTTP 200
{ "status": "OK" }
Kein Datensatz, keine neue ID in der Antwort. Für neue Primärschlüssel vorher get<entity>key aufrufen.
6. Antwort im Fehlerfall
{ "status": "error", "message": "<Fehlertext>" }
| Situation | HTTP-Status | Beispiel message |
|---|---|---|
| Kein/leeres Token | 401 | „Keine Anmeldedaten verfügbar. Bitte neu anmelden.“ |
| Ungültiges/abgelaufenes Token | 401 | „Anmeldung ungültig oder abgelaufen.“ |
| Route ist LocalOnly, Zugriff von außerhalb | 403 | „Zugriff nur vom lokalen Server erlaubt.“ |
| Unbekannte Route | 404 | „Dieser Pfad (/xyz) wurde nicht gefunden.“ |
| Datenbankfehler (Unique-/FK-Verletzung, ungültiges SQL) | 400 | Firebird/InterBase-Fehlermeldung |
| Server-/Konfigurationsfehler | 400 | „Der Parameter [database] fehlt“ |
| Fachlicher Validierungsfehler (nicht erlaubtes Feld, fehlender Pflichtparameter, ungültiges JSON, nicht erlaubtes Sortierfeld, ...) | 200 | „Feld "x" ist nicht erlaubt.“, „"nr" fehlt im Request-Body.“ |
status:"error" geprüft werden.
Prüfreihenfolge im Client
- Antwort als JSON parsen.
statusvorhanden und== "error"? → Fehlerfall,messageanzeigen/loggen.- Sonst → Erfolgsfall: Schreiboperationen anhand
status == "OK", Leseoperationen anhand vorhandenemheader/data(ggf. verschachtelt, siehe 5b). 401/403/404zusätzlich als eindeutige, nicht-fachliche Fehler auswertbar, z. B. für automatischen Re-Login.
7. Beispiele (curl)
Liste ohne Filter
curl -X POST "http://localhost/ibapi/dokumente/getvorlagenfiltered" \
-H "Authorization: Bearer <jwttoken>" \
-H "Content-Type: application/json" \
-d '{"fields":"*"}'
# -> 200, liefert ALLE Vorlagen (kein Filter gesetzt)
Liste mit einem Filter
curl -X POST "http://localhost/ibapi/dokumente/getvorlagenfiltered" \
-H "Authorization: Bearer <jwttoken>" \
-H "Content-Type: application/json" \
-d '{"fields":"*","art":"BRIEF","orderby":"name"}'
# -> 200
# {"header":{"nr":"ftinteger","name":"ftstring 100","art":"ftstring 20"},
# "data":[{"nr":1,"name":"Standardbrief","art":"BRIEF"}]}
Pflichtfeld fehlt fachlicher Fehler → trotzdem 200!
curl -X POST "http://localhost/ibapi/dokumente/getvorlagenbyid" \
-H "Authorization: Bearer <jwttoken>" \
-H "Content-Type: application/json" \
-d '{"fields":"*"}'
# -> 200 (!)
# {"status":"error","message":"\"nr\" fehlt im Request-Body."}
Nicht erlaubtes Feld in fields
curl -X POST "http://localhost/ibapi/dokumente/getvorlagen" \
-H "Authorization: Bearer <jwttoken>" \
-H "Content-Type: application/json" \
-d '{"fields":["nr","geheimesfeld"]}'
# -> 200 (!)
# {"status":"error","message":"Feld \"geheimesfeld\" ist nicht erlaubt."}
Kein/ungültiges Token
curl -X POST "http://localhost/ibapi/dokumente/getvorlagen" \
-H "Content-Type: application/json" -d '{}'
# -> 401
# {"status":"error","message":"Keine Anmeldedaten verfügbar. Bitte neu anmelden."}
8. Pagination – Zusammenfassung
| Body-Parameter | Wirkung |
|---|---|
kein limit bzw. limit:0 | Alle Datensätze, Antwort wie in 5a (kein total-Umschlag) |
limit:N (N > 0), optional offset:M | Antwort wie in 5b, mit total/limit/offset und verschachteltem data.header/data.data |