JSON-RPC
JSON-RPC
POST /{prefix}/{database}/jsonrpc chiama una funzione PostgreSQL e restituisce il risultato come
risposta JSON-RPC 2.0. Questa pagina è il
riferimento; per un primo esempio funzionante vedi Avvio rapido.
1. Richiesta
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}| Campo | Descrizione |
|---|---|
method | Obbligatorio. schema.function, al massimo 256 caratteri. Gli identificatori tra virgolette ("my schema"."my fn") sono ammessi. Lo schema è obbligatorio: hello_world da solo viene rifiutato con 400. capabilities è un metodo integrato (sezione 5). |
params | Qualsiasi valore JSON, passato invariato alla funzione come argomento jsonb. Se manca o è null diventa {}. La convenzione sono gli oggetti; funzionano anche array e scalari. |
id | Qualsiasi valore JSON; viene copiato nella risposta. Se manca, la risposta contiene "id": null (non esistono notifiche fire-and-forget). |
jsonrpc | Per convenzione "2.0"; non viene validato in ingresso, la risposta riporta sempre "2.0". |
idempotencyKey | Estensione opzionale (sezione 7). Al massimo 256 caratteri. |
Le richieste batch (un array JSON di chiamate) non sono supportate e restituiscono 400 — invia una chiamata per richiesta HTTP.
2. Risposta
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}A differenza del JSON-RPC puro su socket, gli errori impostano anche uno stato HTTP significativo, così client e proxy possono reagire
senza analizzare il corpo. L’error.code è 0 a meno che non si applichi un codice JSON-RPC specifico
(-32601 funzione sconosciuta, -32001 permesso negato, -32000 richiesta duplicata).
L’elenco completo è nella pagina Codici di Errore.
3. Scrivere una funzione
Un metodo è una funzione PostgreSQL che accetta esattamente un argomento di tipo jsonb
e restituisce json o jsonb. Le funzioni con altre firme non sono elencate da
capabilities né richiamabili.
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;- Valore restituito. Il risultato JSON viene inviato come
result. UnNULLSQL (come sopra, quando nessuna riga corrisponde) diventa"result": null. La funzione deve restituire JSON valido: un tipo di ritornotextfallisce con 500 a meno che il testo non sia JSON — usato_json('hi'::text)per un risultato stringa. - Errori all’interno della funzione.
RAISE EXCEPTIONo qualsiasi errore SQL interrompe la chiamata con HTTP 500 e il messaggio genericoFunction call failed; il vero messaggio di PostgreSQL viene scritto solo nel log del server, così i dettagli dello schema non trapelano. Segnala gli errori di business attesi (non trovato, validazione) nel valore restituito, per esempio{"ok": false, "reason": "..."}. - Transazioni. Ogni chiamata viene eseguita in una transazione che viene confermata quando la funzione termina e annullata in caso di errore.
- Permessi. La chiamata viene eseguita come il ruolo PostgreSQL autenticato, quindi
GRANT,REVOKEe Row-Level Security decidono cosa può fare il chiamante. Un ruolo ha bisogno diUSAGEsullo schema e diEXECUTEsulla funzione. PostgreSQL concede per impostazione predefinitaEXECUTEsulle nuove funzioni a tutti, quindi revocalo (ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC) e tieni le funzioni esposte in uno schema dedicato — vedi Sicurezza e Autenticazione.
4. Descrivere una funzione per client e agenti AI
La prima riga del commento della funzione è la sua descrizione; un oggetto JSON dopo --- PARAMS --- descrive i parametri
(properties di JSON Schema). Entrambi compaiono in capabilities, nella specifica OpenAPI e 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"}}';Un commento con JSON malformato dopo il marcatore viene ignorato (la funzione ripiega su una descrizione generica dei parametri) e non compromette mai il discovery.
5. Metodi integrati
capabilities
Elenca le funzioni che il ruolo autenticato può chiamare (EXECUTE e USAGE sullo schema); gli schemi di sistema e le funzioni delle estensioni sono nascosti.
Ogni voce ha method, description, parameters, http_method, endpoint e
kind (rpc, oppure file per le funzioni file).
{"jsonrpc":"2.0","method":"capabilities","id":1}Il login per ottenere un JWT non è un metodo JSON-RPC: usa POST /{prefix}/{database}/token con credenziali HTTP Basic (sezione 6).
Il vecchio metodo get_jwt, che riceveva la password in params, è stato rimosso e risponde 404 / -32601 con un rimando al nuovo endpoint.
6. Autenticazione
Ogni richiesta richiede un header Authorization; una richiesta senza viene rifiutata con 401 prima che venga aperta qualsiasi connessione al database.
| Header | Cosa succede |
|---|---|
Basic <base64(user:password)> | Un pool di connessioni autenticato come quell’utente PostgreSQL. Il più semplice; nessun token. Invialo solo tramite HTTPS. |
Bearer <jwt> | Token da POST /{prefix}/{database}/token (o dal tuo identity provider). PgArachne passa al ruolo del token con SET LOCAL ROLE; il ruolo di servizio richiede GRANT user TO pgarachne. |
Bearer <api token> | Token di lunga durata creato con pgarachne.add_api_token(...), associato a un ruolo. Adatto ai servizi. |
Ottenere un JWT: POST /{prefix}/{database}/token
Scambia login e password di PostgreSQL, inviati con autenticazione HTTP Basic, con un JWT. Le credenziali viaggiano nell’header Authorization
— mai in un corpo JSON — e l’endpoint ha un proprio URL, così un reverse proxy può applicare limiti più severi ai login senza leggere i corpi delle richieste.
Richiede JWT_SECRET; altrimenti restituisce 404. Ogni tentativo conta per il limite di frequenza dei login e la risposta non è mai memorizzabile nella cache.
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}'I tentativi falliti sono soggetti a limite di frequenza (HTTP 429). Tutti i dettagli, la durata dei token e un identity provider esterno sono nella pagina Sicurezza e Autenticazione.
7. Idempotenza
Aggiungi "idempotencyKey": "order-42" per rendere sicuro il retry di una chiamata. La prima chiamata con una chiave viene eseguita normalmente; una chiave ripetuta viene rifiutata
prima dell’esecuzione della funzione con HTTP 409 e -32000 «This request has already been processed». La chiave viene salvata nella stessa
transazione della chiamata, quindi una chiamata fallita (e annullata) può essere ritentata con la stessa chiave. Le chiavi hanno un namespace per ruolo.
- Con autenticazione tramite JWT o token API funziona subito, senza configurazione.
- Con l’autenticazione Basic è l’utente stesso a scrivere la chiave, quindi servono
USAGEsullo schemapgarachneeINSERTsupgarachne.requests(altrimenti la chiamata fallisce con 500Idempotency check failed). - Le chiavi vecchie non vengono rimosse automaticamente — pianifica
pgarachne.cleanup_idempotency_keys()(vedi Pulizia delle Chiavi di Idempotenza).
8. Limiti
- Corpo della richiesta:
MAX_REQUEST_BYTES(predefinito 2 MiB), altrimenti 413. methodeidempotencyKey: 256 caratteri ciascuno.- Errori di autenticazione:
LOGIN_RATE_LIMIT/LOGIN_RATE_LIMIT_PER_IPperLOGIN_RATE_WINDOW(vedi Configurazione).
9. Chiamarlo
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}'Da una pagina web usa fetch() con lo stesso header e corpo; una pagina completa da copiare e incollare (inclusi SSE e download di file) è in
Avvio rapido.