JSON-RPC
JSON-RPC
POST /{prefix}/{database}/jsonrpc ruft eine PostgreSQL-Funktion auf und liefert ihr Ergebnis als
JSON-RPC-2.0-Antwort. Diese Seite ist die
Referenz; ein erstes funktionierendes Beispiel finden Sie im Schnellstart.
1. Anfrage
POST /db/my_database/jsonrpc
Authorization: Basic ... (or Bearer <jwt / api token>)
Content-Type: application/json
{"jsonrpc": "2.0", "method": "api.hello_world", "params": {"name": "Alice"}, "id": 1}| Feld | Beschreibung |
|---|---|
method | Pflicht. schema.function, höchstens 256 Zeichen. Quoted Identifier ("my schema"."my fn") sind erlaubt. Das Schema ist zwingend: hello_world allein wird mit 400 abgelehnt. capabilities ist eine eingebaute Methode (Abschnitt 5). |
params | Ein beliebiger JSON-Wert, der unverändert als jsonb-Argument an die Funktion übergeben wird. Fehlt er oder ist er null, wird daraus {}. Üblich sind Objekte; Arrays und Skalare funktionieren ebenfalls. |
id | Ein beliebiger JSON-Wert; er wird in die Antwort übernommen. Fehlt er, enthält die Antwort "id": null (es gibt keine Fire-and-forget-Benachrichtigungen). |
jsonrpc | Üblicherweise "2.0"; wird bei der Eingabe nicht validiert, die Antwort enthält immer "2.0". |
idempotencyKey | Optionale Erweiterung (Abschnitt 7). Höchstens 256 Zeichen. |
Batch-Anfragen (ein JSON-Array von Aufrufen) werden nicht unterstützt und liefern 400 — senden Sie einen Aufruf pro HTTP-Anfrage.
2. Antwort
HTTP 200 {"jsonrpc":"2.0","result":{"message":"Hello, Alice!"},"id":1}
HTTP 404 {"jsonrpc":"2.0","error":{"code":-32601,"message":"Function does not exist"},"id":1}Anders als bei reinem JSON-RPC über einen Socket setzen Fehler zusätzlich einen aussagekräftigen HTTP-Status, sodass Clients und Proxys
reagieren können, ohne den Body zu parsen. Der error.code ist 0, sofern kein spezifischer JSON-RPC-Code zutrifft
(-32601 unbekannte Funktion, -32001 Zugriff verweigert, -32000 doppelte Anfrage).
Die vollständige Liste finden Sie auf der Seite Fehlercodes.
3. Eine Funktion schreiben
Eine Methode ist eine PostgreSQL-Funktion, die genau ein Argument vom Typ jsonb entgegennimmt
und json oder jsonb zurückgibt. Funktionen mit anderen Signaturen werden von
capabilities weder aufgelistet noch sind sie aufrufbar.
CREATE OR REPLACE FUNCTION api.find_user(payload jsonb DEFAULT '{}'::jsonb)
RETURNS json LANGUAGE sql STABLE AS $$
SELECT row_to_json(u) FROM (
SELECT id, name FROM app.users WHERE id = (payload->>'id')::int
) u;
$$;
GRANT USAGE ON SCHEMA api TO web_user;
GRANT EXECUTE ON FUNCTION api.find_user(jsonb) TO web_user;- Rückgabewert. Das JSON-Ergebnis wird als
resultgesendet. Ein SQL-NULL(wie oben, wenn keine Zeile passt) wird zu"result": null. Die Funktion muss gültiges JSON zurückgeben: Ein Rückgabetyptextschlägt mit 500 fehl, es sei denn, der Text ist zufällig JSON — verwenden Sieto_json('hi'::text)für ein String-Ergebnis. - Fehler innerhalb der Funktion.
RAISE EXCEPTIONoder jeder SQL-Fehler bricht den Aufruf mit HTTP 500 und der generischen MeldungFunction call failedab; die echte PostgreSQL-Meldung wird nur ins Serverlog geschrieben, sodass keine Schema-Details nach außen dringen. Melden Sie erwartete fachliche Fehler (nicht gefunden, Validierung) stattdessen im Rückgabewert, zum Beispiel{"ok": false, "reason": "..."}. - Transaktionen. Jeder Aufruf läuft in einer Transaktion, die beim Rückkehren der Funktion committet und bei jedem Fehler zurückgerollt wird.
- Berechtigungen. Der Aufruf läuft als authentifizierte PostgreSQL-Rolle, daher entscheiden
GRANT,REVOKEund Row-Level Security, was ein Aufrufer darf. Eine Rolle benötigtUSAGEauf dem Schema undEXECUTEauf der Funktion. PostgreSQL erteiltEXECUTEauf neue Funktionen standardmäßig an alle, entziehen Sie dieses Recht daher (ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC) und halten Sie freigegebene Funktionen in einem eigenen Schema — siehe Sicherheit & Authentifizierung.
4. Eine Funktion für Clients und KI-Agenten beschreiben
Die erste Zeile des Funktionskommentars ist ihre Beschreibung; ein JSON-Objekt nach --- PARAMS --- beschreibt die Parameter
(JSON-Schema-properties). Beides erscheint in capabilities, in der OpenAPI-Spezifikation und in MCP tools/list:
COMMENT ON FUNCTION api.find_user(jsonb) IS 'Looks up a user by id.
--- PARAMS ---
{"id": {"type": "integer", "description": "User id"}}';Ein Kommentar mit fehlerhaftem JSON nach der Markierung wird ignoriert (die Funktion fällt auf eine generische Parameterbeschreibung zurück) und stört die Erkennung nie.
5. Eingebaute Methoden
capabilities
Listet die Funktionen auf, die die authentifizierte Rolle aufrufen darf (EXECUTE und Schema-USAGE); Systemschemas und Erweiterungsfunktionen sind ausgeblendet.
Jeder Eintrag enthält method, description, parameters, http_method, endpoint und
kind (rpc oder file für Datei-Funktionen).
{"jsonrpc":"2.0","method":"capabilities","id":1}Das Login für ein JWT ist keine JSON-RPC-Methode: Verwenden Sie POST /{prefix}/{database}/token mit HTTP-Basic-Zugangsdaten (Abschnitt 6).
Die alte Methode get_jwt, die das Passwort in params entgegennahm, wurde entfernt und antwortet mit 404 / -32601 samt Hinweis auf den neuen Endpunkt.
6. Authentifizierung
Jede Anfrage benötigt einen Authorization-Header; eine Anfrage ohne ihn wird mit 401 abgelehnt, bevor eine Datenbankverbindung geöffnet wird.
| Header | Was passiert |
|---|---|
Basic <base64(user:password)> | Ein als dieser PostgreSQL-Benutzer authentifizierter Verbindungspool. Am einfachsten; keine Tokens. Nur über HTTPS senden. |
Bearer <jwt> | Token von POST /{prefix}/{database}/token (oder von Ihrem Identity-Provider). PgArachne wechselt mit SET LOCAL ROLE zur Rolle des Tokens; die Service-Rolle benötigt GRANT user TO pgarachne. |
Bearer <api token> | Langlebiges Token, erzeugt mit pgarachne.add_api_token(...) und an eine Rolle gebunden. Gut für Dienste. |
JWT erhalten: POST /{prefix}/{database}/token
Tauscht einen PostgreSQL-Login und ein Passwort, gesendet per HTTP-Basic-Authentifizierung, gegen ein JWT. Die Zugangsdaten reisen im Authorization-Header
— nie in einem JSON-Body — und der Endpunkt hat eine eigene URL, sodass ein Reverse-Proxy strengere Limits für Logins setzen kann, ohne Request-Bodies zu lesen.
Er erfordert JWT_SECRET; andernfalls liefert er 404. Jeder Versuch zählt auf das Login-Ratenlimit, und die Antwort ist nie cachebar.
curl -X POST http://localhost:8080/db/my_database/token -u web_user:password
→ {"token":"eyJhbGciOi...","token_type":"Bearer","expires_in":28800}
curl http://localhost:8080/db/my_database/jsonrpc -H "Authorization: Bearer eyJhbGciOi..." \
-H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"capabilities","id":1}'Fehlgeschlagene Versuche werden ratenbegrenzt (HTTP 429). Alle Details, Token-Laufzeiten und einen externen Identity-Provider finden Sie auf der Seite Sicherheit & Authentifizierung.
7. Idempotenz
Fügen Sie "idempotencyKey": "order-42" hinzu, um einen Aufruf wiederholungssicher zu machen. Der erste Aufruf mit einem Schlüssel läuft normal; ein wiederholter Schlüssel wird
vor dem Ausführen der Funktion mit HTTP 409 und -32000 „This request has already been processed“ abgelehnt. Der Schlüssel wird in derselben
Transaktion wie der Aufruf gespeichert, sodass ein fehlgeschlagener (zurückgerollter) Aufruf mit demselben Schlüssel wiederholt werden kann. Schlüssel sind pro Rolle getrennt.
- Bei Authentifizierung per JWT oder API-Token funktioniert das sofort.
- Bei Basic-Authentifizierung schreibt der Benutzer selbst den Schlüssel, daher benötigt er
USAGEauf dem SchemapgarachneundINSERTaufpgarachne.requests(andernfalls schlägt der Aufruf mit 500Idempotency check failedfehl). - Alte Schlüssel werden nicht automatisch entfernt — planen Sie
pgarachne.cleanup_idempotency_keys()ein (siehe Idempotency Key Cleanup).
8. Limits
- Request-Body:
MAX_REQUEST_BYTES(Standard 2 MiB), sonst 413. methodundidempotencyKey: jeweils 256 Zeichen.- Authentifizierungsfehler:
LOGIN_RATE_LIMIT/LOGIN_RATE_LIMIT_PER_IPproLOGIN_RATE_WINDOW(siehe Konfiguration).
9. Aufrufen
curl -X POST http://localhost:8080/db/my_database/jsonrpc \
-u web_user:password -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"api.find_user","params":{"id":1},"id":1}'Verwenden Sie von einer Webseite aus fetch() mit demselben Header und Body; eine vollständige Seite zum Kopieren und Einfügen (einschließlich SSE und Datei-Download) finden Sie im
Schnellstart.