API
Endpunkte
| Methode · Pfad | Funktion |
|---|---|
POST /api/v1/query | Leseabfrage. Gesamtes Ergebnis als JSON-Array. Bei Erreichen des Limits truncated-Flag |
POST /api/v1/query/stream | Gleiche Abfrage als NDJSON — eine Zeile pro Datensatz |
POST /api/v1/update | Schreibabfrage (TQL wird abgelehnt) |
POST /api/v1/format | Abfrage sortieren · einrücken |
POST /api/v1/cache | Einzelnen In-Memory-Aktualwert abrufen |
GET·POST·DELETE /api/v1/keys · /keys/{id} | API-Key-Liste · Ausstellen · Entsorgen |
GET·POST /api/v1/login | Konsolen-Login — einziger Pfad unter /api/v1, der ohne Key durchgeht |
GET /api/v1/openapi.json | OpenAPI-3.0.3-Dokumentation (Authentifizierung erforderlich) |
GET /api/health | Anonyme Bereitschaftsprüfung — DB · In-Memory · Abfrageprotokoll |
GET /admin/metrics · /ping · /healthcheck · /threads | Betriebskennzahlen (Authentifizierung erforderlich) |
Pfade, die nicht in der Liste stehen, sind fail-closed und geben 401 zurück. Anonym geöffnet sind nur / · /index.html · /api/health · statische Ressourcen sowie /api/v1/login — das ist die Tür, die den Key ausgibt, daher kann sie keinen Key verlangen.
Header
| Header | Erforderlich | Wert |
|---|---|---|
X-API-Key | Ja | Ausgestellter Schlüssel oder konfigurierter Standardschlüssel |
X-Lang | Ja | PQL · CQL · SQL · TQL — alles andere ergibt 400 |
X-Params | Parameterbindung. URL-kodiertes JSON-Objekt | |
Content-Type | Ja | application/json |
Die Abfrage ist der Request-Body selbst. Sie wird nicht in JSON eingepackt.
Lesen — POST /api/v1/query
curl -X POST -H "X-API-Key: ${PP_DATA_GATEWAY_API_KEY}" -H "X-Lang: PQL" \
-H "Content-Type: application/json" \
-d "SELECT tablename FROM pg_tables WHERE schemaname='pp'" \
https://host:5501/api/v1/query
{"result":"SUCCESS","message":"","truncated":false,
"data":[{"tablename":"mm_asset"},{"tablename":"mm_tag"}]}
truncated nicht ignorierenWenn das Ergebnis das Limit (Standardmäßig 100.000 Zeilen) erreicht, bricht das Gateway die Verbindung ab, ohne weiter zu lesen. Wenn truncated auf true gesetzt ist und Sie das nicht beachten, aggregieren Sie mit der Annahme, das abgeschnittene Ergebnis sei vollständig.
Stream — POST /api/v1/query/stream
Für große Ergebnisse ist dies der richtige Weg. Der Server lädt nicht alles in den Speicher, und der Client erhält die erste Zeile sofort.
curl -X POST -H "X-API-Key: ${PP_DATA_GATEWAY_API_KEY}" -H "X-Lang: PQL" \
-H "Content-Type: application/json" \
-d "SELECT tablename FROM pg_tables WHERE schemaname='pp'" \
https://host:5501/api/v1/query/stream
{"row":{"tablename":"mm_asset"}}
{"row":{"tablename":"mm_tag"}}
{"end":{"count":43,"exec_ms":42,"truncated":false}}
Die Antwort ist application/x-ndjson, und wenn während des Streams ein Fehler auftritt, ist die letzte Zeile nicht end, sondern error.
{"error":{"message":"..."}}
end-Zeile unbedingt prüfenDie Authentifizierung endet in der Header-Phase, daher beginnt diese Antwort immer mit 200. Fehler danach erscheinen nur im Body — wenn Sie end nicht erhalten haben, ist das Ergebnis nicht abgeschlossen, sondern die Verbindung wurde getrennt, und wenn end.truncated auf true gesetzt ist, wurde am Limit abgeschnitten.
Parameterbindung — X-Params
Platzhalter in der Abfrage mit :이름 erstellen und in X-Params ein URL-kodiertes JSON-Objekt einfügen.
PARAMS=$(python3 -c "import urllib.parse,json;print(urllib.parse.quote(json.dumps({
'site_id': 'SITE_00001',
'from:ts-long': 1772041960266,
'limit:int': 500
})))")
curl -X POST -H "X-API-Key: ${PP_DATA_GATEWAY_API_KEY}" -H "X-Lang: PQL" \
-H "X-Params: ${PARAMS}" -H "Content-Type: application/json" \
-d "SELECT * FROM pp.mm_tag WHERE site_id = :site_id AND created_at >= :from LIMIT :limit" \
https://host:5501/api/v1/query
Fügen Sie dem Schlüssel :타입 an, um die Konvertierung anzugeben. Ohne Suffix wird der JSON-Typ unverändert übernommen.
| Suffix | Bedeutung |
|---|---|
:int · :long · :float · :double | Zahl |
:string | Zeichenkette |
:epoch · :ts-long | Epoch-Millisekunden → Zeitstempel |
:ts-string | Datumszeichenkette → Zeitstempel |
Unbekannte Typsuffixe führen zu 400 PARAM_MISSING — sie werden nicht stillschweigend ignoriert.
X-Params defekt ist, ist es 400, nicht 500Nicht dekodierbare Werte (%zz) oder Nicht-JSON-Werte sind Client-Fehler. Früher trat dies als 500 INTERNAL_ERROR auf und wurde als „Server ausgefallen“ gelesen.
Schreiben — POST /api/v1/update
curl -X POST -H "X-API-Key: ${PP_DATA_GATEWAY_API_KEY}" -H "X-Lang: PQL" \
-H "Content-Type: application/json" \
-d "UPDATE pp.mm_tag SET description = 'reactor inlet' WHERE tag_id = 'TAG_001'" \
https://host:5501/api/v1/update
TQL wird auf diesem Pfad abgelehnt — die Zeitreihen-Engine akzeptiert keine Schreibvorgänge über das Gateway.
Fehlervertrag
Alle Fehler kommen im selben Umschlag.
{"result":"ERROR","error_code":"QUERY_SYNTAX",
"message":"DDL statements are not allowed: DROP"}
error_code | HTTP | Bedeutung |
|---|---|---|
AUTH_MISSING | 401 | X-API-Key-Header fehlt |
AUTH_INVALID | 401 | Schlüssel stimmt nicht |
PARAM_MISSING | 400 | Header · Sprache · Parametertyp-Konvertierung fehlgeschlagen |
QUERY_SYNTAX | 400 | DDL abgelehnt · Mehrfachanweisung · Syntaxfehler |
QUERY_DATA | 400 | Datenbeschränkung verletzt |
QUERY_CONSTRAINT | 409 | Integritätsbeschränkung |
QUERY_DEADLOCK | 409 | Deadlock |
QUERY_AUTH | 401 | Authentifizierungsfehler im Backend-Speicher |
QUERY_CONNECTION | 503 | Keine Verbindung zum Backend |
QUERY_TIMEOUT | 504 | Backend-Antwort zu langsam |
QUERY_ERROR | 500 | Sonstiger Ausführungsfehler |
QUERY_UNSUPPORTED | 400 | Anforderung, die diese Sprache nicht unterstützt |
NOT_FOUND | 404 | Pfad nicht vorhanden |
METHOD_NOT_ALLOWED | 405 | Methode nicht vorhanden |
UNSUPPORTED_MEDIA_TYPE | 415 | Content-Type-Abweichung |
API_KEY_NOT_CONFIGURED | 503 | Master-Key nicht konfiguriert — kein Anmeldefehler |
LOGIN_NOT_CONFIGURED | 503 | Konsole-Konto: beide Schlüssel nicht konfiguriert |
Syntax · Beschränkung · Daten · Authentifizierung sind Client-Ursachen (4xx), und Verbindung · Timeout sind Infrastruktur-Ursachen (5xx). Wenn beides vermischt wird, kann der Aufrufer nicht unterscheiden, ob „meine Abfrage falsch war“ oder „der Speicher ausgefallen ist“. Auch die Server-Logs trennen nach diesem Kriterium: 4xx als WARN, 5xx als ERROR.
Es gibt zwei API-Keys
| Typ | Wo | Lebensdauer |
|---|---|---|
| Standardschlüssel | Konfigurationsdatei data.gateway.api.key (gerendert) | Bis zur Konfigurationsänderung |
| Ausgestellter Schlüssel | Erstellt über POST /api/v1/keys. Gespeichert wird nur der Salt + SHA-256-Digest | Lebt nur im Prozess — verschwindet bei Neustart |
Der Vergleich erfolgt immer in konstanter Zeit.
Bei erfolgreichem Login wird der konfigurierte Standardschlüssel unverändert zurückgegeben. Der Grund ist Bootstrap — bei einer Neuinstallation ist der Key-Speicher leer, und jeder Pfad, der einen Key erstellt, erfordert bereits einen Key. Der erste Eindringling muss über Anmeldeinformationen verfügen, die „vor“ dem Speicher existieren, und das ist nur der Schlüssel aus der Konfigurationsdatei.
Daher liest oder schreibt dieser Pfad weder den Key-Speicher noch schreibt er ihn. Er ist die Tür, die auch bei einem Ausfall der DB geöffnet bleiben muss.