JSON-RPC

5 хв читання

JSON-RPC

POST /{prefix}/{database}/jsonrpc викликає функцію PostgreSQL і повертає її результат як відповідь JSON-RPC 2.0. Ця сторінка є довідником; перший робочий приклад дивіться в розділі Швидкий старт.

1. Запит

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

{"jsonrpc": "2.0", "method": "api.hello_world", "params": {"name": "Alice"}, "id": 1}
ПолеОпис
methodОбов’язкове. schema.function, не більше 256 символів. Допускаються ідентифікатори в лапках ("my schema"."my fn"). Схема обов’язкова: самотнє hello_world відхиляється з кодом 400. capabilities — вбудований метод (розділ 5).
paramsБудь-яке значення JSON, яке передається функції без змін як її аргумент jsonb. Відсутнє або null стає {}. Загальноприйняті об’єкти; масиви та скалярні значення також працюють.
idБудь-яке значення JSON; копіюється у відповідь. Якщо його немає, відповідь містить "id": null (сповіщень типу fire-and-forget не існує).
jsonrpcЗазвичай "2.0"; на вході не перевіряється, відповідь завжди містить "2.0".
idempotencyKeyНеобов’язкове розширення (розділ 7). Не більше 256 символів.

Пакетні запити (масив JSON з викликами) не підтримуються й повертають 400 — надсилайте один виклик в одному HTTP-запиті.

2. Відповідь

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}

На відміну від звичайного JSON-RPC через сокет, помилки також встановлюють змістовний HTTP-статус, тож клієнти й проксі можуть реагувати без розбору тіла. error.code дорівнює 0, якщо не застосовується конкретний код JSON-RPC (-32601 невідома функція, -32001 відмовлено в доступі, -32000 дубльований запит). Повний список є на сторінці Коди помилок.

3. Написання функції

Метод — це функція PostgreSQL, яка приймає рівно один аргумент типу jsonb і повертає json або jsonb. Функції з іншими сигнатурами не перелічуються в capabilities і не можуть бути викликані.

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;
  • Значення, що повертається. Результат JSON надсилається як result. SQL-значення NULL (як вище, коли жоден рядок не підходить) стає "result": null. Функція має повертати коректний JSON: тип text призводить до помилки 500, якщо текст випадково не є JSON — для рядкового результату використовуйте to_json('hi'::text).
  • Помилки всередині функції. RAISE EXCEPTION або будь-яка помилка SQL перериває виклик з HTTP 500 і загальним повідомленням Function call failed; справжнє повідомлення PostgreSQL записується лише в журнал сервера, тож подробиці схеми не витікають. Очікувані бізнес-помилки (не знайдено, валідація) повідомляйте радше в значенні, що повертається, наприклад {"ok": false, "reason": "..."}.
  • Транзакції. Кожен виклик виконується в одній транзакції, яка фіксується, коли функція повертає результат, і відкочується за будь-якої помилки.
  • Права доступу. Виклик виконується від імені автентифікованої ролі PostgreSQL, тож GRANT, REVOKE і Row-Level Security визначають, що дозволено викликачу. Ролі потрібні USAGE на схему та EXECUTE на функцію. PostgreSQL за замовчуванням надає EXECUTE на нові функції всім, тому скасуйте це (ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC) і тримайте відкриті функції в окремій схемі — див. Безпека та автентифікація.

4. Опис функції для клієнтів і ШІ-агентів

Перший рядок коментаря функції є її описом; об’єкт JSON після --- PARAMS --- описує параметри (properties у JSON Schema). Обидва з’являються в capabilities, специфікації OpenAPI та MCP tools/list:

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

Коментар із некоректним JSON після маркера ігнорується (функція повертається до загального опису параметрів) і ніколи не ламає виявлення.

5. Вбудовані методи

capabilities

Перелічує функції, які автентифікована роль може викликати (EXECUTE і USAGE на схему); системні схеми та функції розширень приховані. Кожен запис містить method, description, parameters, http_method, endpoint і kind (rpc, або file для файлових функцій).

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

Отримання JWT не є методом JSON-RPC: використовуйте POST /{prefix}/{database}/token з обліковими даними HTTP Basic (розділ 6). Старий метод get_jwt, що приймав пароль у params, видалено; він відповідає 404 / -32601 із вказівкою на новий endpoint.

6. Автентифікація

Кожен запит потребує заголовка Authorization; запит без нього відхиляється з кодом 401 ще до відкриття будь-якого з’єднання з базою даних.

ЗаголовокЩо відбувається
Basic <base64(user:password)>Пул з’єднань, автентифікований як цей користувач PostgreSQL. Найпростіше; без токенів. Надсилайте лише через HTTPS.
Bearer <jwt>Токен із POST /{prefix}/{database}/token (або від вашого провайдера ідентичності). PgArachne перемикається на роль із токена через SET LOCAL ROLE; службовій ролі потрібен GRANT user TO pgarachne.
Bearer <api token>Довготривалий токен, створений через pgarachne.add_api_token(...) і прив’язаний до ролі. Добре підходить для сервісів.

Отримання JWT: POST /{prefix}/{database}/token

Обмінює логін і пароль PostgreSQL, надіслані через автентифікацію HTTP Basic, на JWT. Облікові дані передаються в заголовку Authorization — ніколи в тілі JSON — а endpoint має власну URL-адресу, тож зворотний проксі може застосовувати суворіші ліміти до входів, не читаючи тіла запитів. Потребує JWT_SECRET; інакше повертає 404. Кожна спроба враховується в ліміті спроб входу, а відповідь ніколи не кешується.

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

Невдалі спроби обмежуються лімітом (HTTP 429). Усі подробиці, строки дії токенів і зовнішній провайдер ідентичності описані на сторінці Безпека та автентифікація.

7. Ідемпотентність

Додайте "idempotencyKey": "order-42", щоб виклик можна було безпечно повторити. Перший виклик із ключем виконується як звичайно; повторений ключ відхиляється до запуску функції з HTTP 409 і -32000 «This request has already been processed». Ключ зберігається в тій самій транзакції, що й виклик, тож виклик, який завершився помилкою (і був відкочений), можна повторити з тим самим ключем. Ключі розділені за ролями.

  • Із автентифікацією JWT або токеном API це працює з коробки.
  • З автентифікацією Basic ключ записує сам користувач, тому йому потрібні USAGE на схему pgarachne та INSERT на pgarachne.requests (інакше виклик завершується помилкою 500 Idempotency check failed).
  • Старі ключі не видаляються автоматично — заплануйте pgarachne.cleanup_idempotency_keys() (див. Очищення ключів ідемпотентності).

8. Обмеження

  • Тіло запиту: MAX_REQUEST_BYTES (за замовчуванням 2 МіБ), інакше 413.
  • method та idempotencyKey: по 256 символів.
  • Збої автентифікації: LOGIN_RATE_LIMIT / LOGIN_RATE_LIMIT_PER_IP на LOGIN_RATE_WINDOW (див. Конфігурація).

9. Виклик

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

З веб-сторінки використовуйте fetch() з тим самим заголовком і тілом; повна сторінка для копіювання та вставлення (разом із SSE і завантаженням файлів) є в розділі Швидкий старт.