JSON-RPC
JSON-RPC
POST /{prefix}/{database}/jsonrpc llama a una función de PostgreSQL y devuelve su resultado como una
respuesta JSON-RPC 2.0. Esta página es la
referencia; para un primer ejemplo funcional consulta Inicio rápido.
1. Petición
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 | Descripción |
|---|---|
method | Obligatorio. schema.function, como máximo 256 caracteres. Se permiten identificadores entre comillas ("my schema"."my fn"). El esquema es obligatorio: hello_world solo se rechaza con 400. capabilities es un método integrado (sección 5). |
params | Cualquier valor JSON, pasado sin cambios a la función como su argumento jsonb. Si falta o es null, se convierte en {}. Por convención son objetos; los arrays y escalares también funcionan. |
id | Cualquier valor JSON; se copia en la respuesta. Si falta, la respuesta contiene "id": null (no hay notificaciones de tipo «dispara y olvida»). |
jsonrpc | Por convención "2.0"; no se valida en la entrada, la respuesta siempre lleva "2.0". |
idempotencyKey | Extensión opcional (sección 7). Como máximo 256 caracteres. |
Las peticiones por lotes (un array JSON de llamadas) no están soportadas y devuelven 400 — envía una llamada por petición HTTP.
2. Respuesta
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 diferencia de JSON-RPC puro sobre un socket, los errores también establecen un estado HTTP significativo, de modo que clientes y proxies pueden reaccionar
sin analizar el cuerpo. El error.code es 0 salvo que se aplique un código JSON-RPC específico
(-32601 función desconocida, -32001 permiso denegado, -32000 petición duplicada).
La lista completa está en la página Códigos de Error.
3. Escribir una función
Un método es una función de PostgreSQL que recibe exactamente un argumento de tipo jsonb
y devuelve json o jsonb. Las funciones con otras firmas ni aparecen en
capabilities ni se pueden llamar.
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;- Valor de retorno. El resultado JSON se envía como
result. UnNULLde SQL (como arriba, cuando ninguna fila coincide) se convierte en"result": null. La función debe devolver JSON válido: un tipo de retornotextfalla con 500 salvo que el texto sea JSON — usato_json('hi'::text)para un resultado de tipo cadena. - Errores dentro de la función.
RAISE EXCEPTIONo cualquier error SQL aborta la llamada con HTTP 500 y el mensaje genéricoFunction call failed; el mensaje real de PostgreSQL solo se escribe en el log del servidor, así que no se filtran detalles del esquema. Informa de los errores de negocio esperados (no encontrado, validación) en el valor de retorno, por ejemplo{"ok": false, "reason": "..."}. - Transacciones. Cada llamada se ejecuta en una transacción que se confirma cuando la función retorna y se revierte ante cualquier error.
- Permisos. La llamada se ejecuta como el rol de PostgreSQL autenticado, así que
GRANT,REVOKEy Row-Level Security deciden qué puede hacer quien llama. Un rol necesitaUSAGEen el esquema yEXECUTEen la función. PostgreSQL concedeEXECUTEsobre las funciones nuevas a todos por defecto, así que revócalo (ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC) y mantén las funciones expuestas en un esquema dedicado — consulta Seguridad y Autenticación.
4. Describir una función para clientes y agentes de IA
La primera línea del comentario de la función es su descripción; un objeto JSON después de --- PARAMS --- describe los parámetros
(properties de JSON Schema). Ambos aparecen en capabilities, en la especificación OpenAPI y en 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 comentario con JSON mal formado después del marcador se ignora (la función recurre a una descripción genérica de parámetros) y nunca rompe el descubrimiento.
5. Métodos integrados
capabilities
Lista las funciones que el rol autenticado puede llamar (EXECUTE y USAGE del esquema); los esquemas del sistema y las funciones de extensiones están ocultos.
Cada entrada tiene method, description, parameters, http_method, endpoint y
kind (rpc, o file para funciones de archivo).
{"jsonrpc":"2.0","method":"capabilities","id":1}Iniciar sesión para obtener un JWT no es un método JSON-RPC: usa POST /{prefix}/{database}/token con credenciales HTTP Basic (sección 6).
El antiguo método get_jwt, que recibía la contraseña en params, fue eliminado y responde 404 / -32601 con una indicación del nuevo endpoint.
6. Autenticación
Toda petición necesita una cabecera Authorization; una petición sin ella se rechaza con 401 antes de abrir cualquier conexión a la base de datos.
| Cabecera | Qué ocurre |
|---|---|
Basic <base64(user:password)> | Un pool de conexiones autenticado como ese usuario de PostgreSQL. Lo más simple; sin tokens. Envíalo solo por HTTPS. |
Bearer <jwt> | Token de POST /{prefix}/{database}/token (o de tu propio proveedor de identidad). PgArachne cambia al rol del token con SET LOCAL ROLE; el rol de servicio necesita GRANT user TO pgarachne. |
Bearer <api token> | Token de larga duración creado con pgarachne.add_api_token(...), vinculado a un rol. Adecuado para servicios. |
Obtener un JWT: POST /{prefix}/{database}/token
Intercambia un login y contraseña de PostgreSQL, enviados con autenticación HTTP Basic, por un JWT. Las credenciales viajan en la cabecera Authorization
— nunca en un cuerpo JSON — y el endpoint tiene su propia URL, de modo que un proxy inverso puede aplicar límites más estrictos a los inicios de sesión sin leer cuerpos de petición.
Requiere JWT_SECRET; en caso contrario devuelve 404. Cada intento cuenta para el límite de frecuencia de inicio de sesión y la respuesta nunca se puede almacenar en caché.
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}'Los intentos fallidos tienen límite de frecuencia (HTTP 429). Todos los detalles, la duración de los tokens y un proveedor de identidad externo están en la página Seguridad y Autenticación.
7. Idempotencia
Añade "idempotencyKey": "order-42" para que una llamada se pueda reintentar con seguridad. La primera llamada con una clave se ejecuta con normalidad; una clave repetida se rechaza
antes de ejecutar la función con HTTP 409 y -32000 «This request has already been processed». La clave se guarda en la misma
transacción que la llamada, así que una llamada que falló (y se revirtió) puede reintentarse con la misma clave. Las claves están en un espacio de nombres por rol.
- Con autenticación por JWT o token de API funciona sin configuración adicional.
- Con autenticación Basic el propio usuario escribe la clave, así que necesita
USAGEen el esquemapgarachneeINSERTenpgarachne.requests(de lo contrario la llamada falla con 500Idempotency check failed). - Las claves antiguas no se eliminan automáticamente — programa
pgarachne.cleanup_idempotency_keys()(consulta Limpieza de Claves de Idempotencia).
8. Límites
- Cuerpo de la petición:
MAX_REQUEST_BYTES(por defecto 2 MiB); si se supera, 413. methodeidempotencyKey: 256 caracteres cada uno.- Fallos de autenticación:
LOGIN_RATE_LIMIT/LOGIN_RATE_LIMIT_PER_IPporLOGIN_RATE_WINDOW(consulta Configuración).
9. Llamarlo
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}'Desde una página web usa fetch() con la misma cabecera y cuerpo; una página completa para copiar y pegar (incluidos SSE y descarga de archivos) está en
Inicio rápido.