JSON-RPC

5 min czytania

JSON-RPC

POST /{prefix}/{database}/jsonrpc wywołuje funkcję PostgreSQL i zwraca jej wynik jako odpowiedź JSON-RPC 2.0. Ta strona jest opisem referencyjnym; pierwszy działający przykład znajdziesz w Szybkim starcie.

1. Żądanie

POST /db/my_database/jsonrpc
Authorization: Basic ...            (lub Bearer <jwt / api token>)
Content-Type: application/json

{"jsonrpc": "2.0", "method": "api.hello_world", "params": {"name": "Alice"}, "id": 1}
PoleOpis
methodWymagane. schema.function, najwyżej 256 znaków. Dozwolone są identyfikatory w cudzysłowie ("my schema"."my fn"). Schemat jest obowiązkowy: samo hello_world jest odrzucane kodem 400. capabilities to metoda wbudowana (sekcja 5).
paramsDowolna wartość JSON, przekazywana funkcji bez zmian jako jej argument jsonb. Brak lub null staje się {}. Konwencją są obiekty; tablice i wartości skalarne również działają.
idDowolna wartość JSON; jest kopiowana do odpowiedzi. Jeśli jej brak, odpowiedź zawiera "id": null (nie ma powiadomień typu fire-and-forget).
jsonrpcZwykle "2.0"; nie jest walidowane na wejściu, odpowiedź zawsze niesie "2.0".
idempotencyKeyOpcjonalne rozszerzenie (sekcja 7). Najwyżej 256 znaków.

Żądania wsadowe (tablica JSON z wywołaniami) nie są obsługiwane i zwracają 400 — wysyłaj jedno wywołanie na żądanie HTTP.

2. Odpowiedź

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}

W odróżnieniu od zwykłego JSON-RPC przez gniazdo, błędy ustawiają także sensowny status HTTP, więc klienci i proxy mogą reagować bez parsowania treści. error.code wynosi 0, chyba że ma zastosowanie konkretny kod JSON-RPC (-32601 nieznana funkcja, -32001 brak uprawnień, -32000 zduplikowane żądanie). Pełną listę znajdziesz na stronie Kody błędów.

3. Pisanie funkcji

Metoda to funkcja PostgreSQL przyjmująca dokładnie jeden argument typu jsonb i zwracająca json lub jsonb. Funkcje o innych sygnaturach nie są wymieniane przez capabilities ani nie da się ich wywołać.

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;
  • Wartość zwracana. Wynik JSON jest wysyłany jako result. SQL-owe NULL (jak wyżej, gdy żaden wiersz nie pasuje) staje się "result": null. Funkcja musi zwracać poprawny JSON: zwracany typ text kończy się błędem 500, chyba że tekst akurat jest JSON-em — dla wyniku w postaci napisu użyj to_json('hi'::text).
  • Błędy wewnątrz funkcji. RAISE EXCEPTION lub dowolny błąd SQL przerywa wywołanie kodem HTTP 500 i ogólnym komunikatem Function call failed; prawdziwy komunikat PostgreSQL trafia tylko do logu serwera, więc szczegóły schematu nie wyciekają. Oczekiwane błędy biznesowe (nie znaleziono, walidacja) zgłaszaj raczej w wartości zwracanej, na przykład {"ok": false, "reason": "..."}.
  • Transakcje. Każde wywołanie działa w jednej transakcji, zatwierdzanej, gdy funkcja zwróci wynik, i wycofywanej przy każdym błędzie.
  • Uprawnienia. Wywołanie działa jako uwierzytelniona rola PostgreSQL, więc GRANT, REVOKE i Row-Level Security decydują, co wolno wywołującemu. Rola potrzebuje USAGE na schemacie i EXECUTE na funkcji. PostgreSQL domyślnie nadaje EXECUTE na nowych funkcjach wszystkim, więc cofnij to (ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC) i trzymaj udostępniane funkcje w dedykowanym schemacie — zobacz Bezpieczeństwo i uwierzytelnianie.

4. Opisywanie funkcji dla klientów i agentów AI

Pierwsza linia komentarza funkcji jest jej opisem; obiekt JSON po --- PARAMS --- opisuje parametry (properties w JSON Schema). Oba pojawiają się w capabilities, w specyfikacji OpenAPI i w MCP tools/list:

COMMENT ON FUNCTION api.find_user(jsonb) IS 'Looks up a user by id.
--- PARAMS ---
{"id": {"type": "integer", "description": "User id"}}';

Komentarz z błędnym JSON-em po znaczniku jest ignorowany (funkcja wraca do ogólnego opisu parametrów) i nigdy nie psuje wykrywania.

5. Wbudowane metody

capabilities

Wyświetla funkcje, które uwierzytelniona rola może wywołać (EXECUTE i USAGE na schemacie); schematy systemowe i funkcje rozszerzeń są ukryte. Każdy wpis ma method, description, parameters, http_method, endpoint oraz kind (rpc lub file dla funkcji plikowych).

{"jsonrpc":"2.0","method":"capabilities","id":1}

Logowanie po JWT nie jest metodą JSON-RPC: użyj POST /{prefix}/{database}/token z danymi HTTP Basic (sekcja 6). Dawna metoda get_jwt, która przyjmowała hasło w params, została usunięta i odpowiada 404 / -32601 ze wskazaniem nowego endpointu.

6. Uwierzytelnianie

Każde żądanie wymaga nagłówka Authorization; żądanie bez niego jest odrzucane kodem 401, zanim zostanie otwarte jakiekolwiek połączenie z bazą danych.

NagłówekCo się dzieje
Basic <base64(user:password)>Pula połączeń uwierzytelniona jako ten użytkownik PostgreSQL. Najprostsze; bez tokenów. Wysyłaj tylko przez HTTPS.
Bearer <jwt>Token z POST /{prefix}/{database}/token (lub z własnego dostawcy tożsamości). PgArachne przełącza się na rolę z tokenu przez SET LOCAL ROLE; rola serwisowa potrzebuje GRANT user TO pgarachne.
Bearer <api token>Długotrwały token utworzony przez pgarachne.add_api_token(...), powiązany z rolą. Dobry dla usług.

Pobieranie JWT: POST /{prefix}/{database}/token

Wymienia login i hasło PostgreSQL, wysłane przez uwierzytelnianie HTTP Basic, na JWT. Dane logowania podróżują w nagłówku Authorization — nigdy w treści JSON — a endpoint ma własny adres URL, więc reverse proxy może stosować surowsze limity dla logowań bez odczytywania treści żądań. Wymaga JWT_SECRET; w przeciwnym razie zwraca 404. Każda próba wlicza się do limitu prób logowania, a odpowiedź nigdy nie może być cache’owana.

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}'

Nieudane próby są ograniczane limitem (HTTP 429). Wszystkie szczegóły, czasy życia tokenów i zewnętrzny dostawca tożsamości są na stronie Bezpieczeństwo i uwierzytelnianie.

7. Idempotencja

Dodaj "idempotencyKey": "order-42", aby wywołanie można było bezpiecznie ponowić. Pierwsze wywołanie z danym kluczem wykonuje się normalnie; powtórzony klucz jest odrzucany przed uruchomieniem funkcji kodem HTTP 409 i -32000 „This request has already been processed". Klucz jest zapisywany w tej samej transakcji co wywołanie, więc wywołanie, które się nie powiodło (i zostało wycofane), można ponowić z tym samym kluczem. Klucze są rozdzielone per rola.

  • Przy uwierzytelnianiu JWT lub tokenem API działa to od razu.
  • Przy uwierzytelnianiu Basic klucz zapisuje sam użytkownik, więc potrzebuje USAGE na schemacie pgarachne oraz INSERT na pgarachne.requests (w przeciwnym razie wywołanie kończy się błędem 500 Idempotency check failed).
  • Stare klucze nie są usuwane automatycznie — zaplanuj pgarachne.cleanup_idempotency_keys() (zobacz Czyszczenie kluczy idempotencji).

8. Limity

  • Treść żądania: MAX_REQUEST_BYTES (domyślnie 2 MiB), w przeciwnym razie 413.
  • method i idempotencyKey: po 256 znaków.
  • Niepowodzenia uwierzytelniania: LOGIN_RATE_LIMIT / LOGIN_RATE_LIMIT_PER_IP na LOGIN_RATE_WINDOW (zobacz Konfiguracja).

9. Wywołanie

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}'

Ze strony internetowej użyj fetch() z tym samym nagłówkiem i treścią; kompletna strona do skopiowania i wklejenia (wraz z SSE i pobieraniem plików) jest w Szybkim starcie.