JSON-RPC 2.0 support
Motivation
Many APIs speak JSON-RPC 2.0 over HTTP. You can call them with unihttp today, but every project ends up re-writing the same envelope / id / error handling by hand.
It would be nice to have this out of the box — including batch requests, which are the hard part to do yourself.
What we want
Full JSON-RPC 2.0 over HTTP, in one PR:
- Declare a JSON-RPC call in the same declarative style as a regular unihttp method
- Get a typed result back, or a proper exception carrying
code / message / data when the server returns error
- Named and positional params — both
"params": {"a": 1, "b": 2} and "params": [1, 2] (Ethereum and Bitcoin Core use the positional form almost exclusively)
- Notifications — requests without
id, for which the server sends no response
- Batch requests — several calls in one HTTP request, with a typed result (or an error) for each of them; notifications may be part of a batch
Constraints
- No changes to the core if possible. The existing
BaseMethod hooks (build_http_request, validate_response, make_response) look sufficient — a new unihttp/jsonrpc/ package is the expected shape. If you think a core change is really needed, let's discuss it first.
- Works with both sync and async clients, with
bind_method and call_method
- Middleware (retries, auth, logging) keeps working, and sees a batch as a single HTTP request
- Follows the JSON-RPC 2.0 spec. In particular:
- batch responses may come back in any order and must be matched by
id
- notifications get no entry in a batch response; a batch of only notifications gets no response body at all
- a server may reject a whole batch with a single error object instead of an array
- Works with every serialization backend (adaptix, pydantic, msgspec)
- Tests, a docs page and an example in
examples/
Open questions
The API is intentionally not fixed — this is the part we'd like your ideas on:
- Where does the RPC method name live? A class attribute, the
__url__, something else?
- How are positional params and notifications declared? Class-level flags, separate base classes, markers?
- Where does
id come from? Per method, per client, a counter, a UUID? Should it be configurable?
- How do batch results reach the caller with types preserved? Future-like handles, a
gather-style tuple, something else?
- Partial failures in a batch: one call failed, the others succeeded — what does the caller see?
- Headers / query params in a batch: child calls may declare different ones, but there is only one HTTP request.
- Non-JSON error responses (e.g. an HTML 502 page): how should this interact with the existing
on_error / handle_error flow?
How to contribute
Please post a short API sketch in a comment before writing code — a few lines showing how a method is declared, how it's called, and how a batch looks. We'll agree on the direction here, so nobody spends a weekend on a PR that needs to be redone.
Partial ideas are welcome too — you don't need to answer every question above.
Out of scope
- Non-HTTP transports (WebSocket, stdio)
References
JSON-RPC 2.0 support
Motivation
Many APIs speak JSON-RPC 2.0 over HTTP. You can call them with unihttp today, but every project ends up re-writing the same envelope /
id/ error handling by hand.It would be nice to have this out of the box — including batch requests, which are the hard part to do yourself.
What we want
Full JSON-RPC 2.0 over HTTP, in one PR:
code/message/datawhen the server returnserror"params": {"a": 1, "b": 2}and"params": [1, 2](Ethereum and Bitcoin Core use the positional form almost exclusively)id, for which the server sends no responseConstraints
BaseMethodhooks (build_http_request,validate_response,make_response) look sufficient — a newunihttp/jsonrpc/package is the expected shape. If you think a core change is really needed, let's discuss it first.bind_methodandcall_methodidexamples/Open questions
The API is intentionally not fixed — this is the part we'd like your ideas on:
__url__, something else?idcome from? Per method, per client, a counter, a UUID? Should it be configurable?gather-style tuple, something else?on_error/handle_errorflow?How to contribute
Please post a short API sketch in a comment before writing code — a few lines showing how a method is declared, how it's called, and how a batch looks. We'll agree on the direction here, so nobody spends a weekend on a PR that needs to be redone.
Partial ideas are welcome too — you don't need to answer every question above.
Out of scope
References