1 Why consider JSON‑RPC?
JSON‑RPC is a lightweight Remote Procedure Call protocol that serialises every request/response as a single JSON object and is transport‑agnostic (HTTP, WebSocket, raw TCP, message queues…). Compared with REST it removes URL/verb semantics and lets you call a remote function almost like a local one; compared with gRPC it keeps human‑readable payloads and a near‑zero learning curve.
Typical request:
{ "jsonrpc": "2.0", "method": "add", "params": [5, 3], "id": 1 }
Typical success reply:
{ "jsonrpc": "2.0", "result": 8, "id": 1 }
If id
is omitted the call is a “notification” and the server must not reply.
2 Protocol in one picture (PlantUML)
@startuml
actor "Service A\n(client)" as A
participant "Service B\n(server)" as B
A -> B : POST /rpc \n{jsonrpc:2.0, method:add, params:[5,3], id:1}
B -> B : add(5,3)
B --> A : {jsonrpc:2.0, result:8, id:1}
@enduml
Batching (array of calls in a single POST) and notifications (fire‑and‑forget) are part of the 2.0 spec.
3 Quick comparison
JSON‑RPC | REST | gRPC | |
---|---|---|---|
Message format | JSON (text) | JSON / XML | Protobuf (binary) |
API model | Functions | Resources (noun + verb) | Functions |
Streaming | ✗ | ✗ | ✓ (HTTP/2) |
Human readable | ✓ | ✓ | ✗ |
Code‑gen / IDL | None | Swagger / OpenAPI | .proto |
Raw performance | ◑ | ◑ | ◎ (fastest) |
Tooling mind‑share | Medium | Huge | High |
Use JSON‑RPC when the call graph is naturally verb‑centric (micro‑services cooperating via small, well‑defined procedures) and you want a payload anyone can read with curl
.
4 Library landscape (2025)
Library | Latest release | Python support | Django support | Notes |
---|---|---|---|---|
django‑modern‑rpc 1.1.0 | Jan 2025 | 3.7 → 3.13 | 2.2 → 5.1 | Multi‑entry‑point, JSON‑RPC 2.0 only, built‑in auth & HTML docs |
tinyrpc 1.1.7 | Jul 2023 | 3.x | framework‑agnostic | Modular transports (HTTP, WS, ZeroMQ, RabbitMQ, …) |
jsonrpcserver 5.0.9 | Sep 2022 | 3.8 → 3.10 | — | One‑file HTTP helpers, good for small services |
json‑rpc 1.15.0 | Jun 2023 | 2.6 → 3.8 (classifiers); runs 3.9+ | Optional Django 1.x | Pure protocol implementation, use when you embed your own transport |
Recommendation
Django 4/5 projects: django‑modern‑rpc.
Framework‑mix or multiple transports: tinyrpc.
Tiny one‑off HTTP service:jsonrpcserver
.
5 Hands‑on code
5.1 Python standalone server & client (tinyrpc)
# server.py (gevent WSGI HTTP)
from gevent import pywsgi, queue
from tinyrpc.server.gevent import RPCServerGreenlets
from tinyrpc.transports.wsgi import WsgiServerTransport
from tinyrpc.protocols.jsonrpc import JSONRPCProtocol
from tinyrpc.dispatch import RPCDispatcher
disp = RPCDispatcher()
@disp.public
def add(a: int, b: int) -> int:
return a + b
transport = WsgiServerTransport(queue_class=queue.Queue)
wsgi = pywsgi.WSGIServer(("0.0.0.0", 5000), transport.handle)
rpc_srv = RPCServerGreenlets(transport, JSONRPCProtocol(), disp)
wsgi.start()
rpc_srv.serve_forever()
# client.py
from tinyrpc import RPCClient
from tinyrpc.protocols.jsonrpc import JSONRPCProtocol
from tinyrpc.transports.http import HttpPostClientTransport
rpc = RPCClient(JSONRPCProtocol(),
HttpPostClientTransport("http://localhost:5000/"))
proxy = rpc.get_proxy()
print(proxy.add(5, 3)) # → 8
5.2 Django 4/5 with django‑modern‑rpc
pip install django-modern-rpc
# settings.py
INSTALLED_APPS += ["modernrpc"]
MODERNRPC_METHODS_MODULES = [
'rpcapp.rpc_methods',
]
# urls.py
from modernrpc.views import JSONRPCEntryPoint
urlpatterns += [
path("rpc/", JSONRPCEntryPoint.as_view(), name="jsonrpc"),
]
# myapp/rpc_methods.py
from modernrpc.core import rpc_method
@rpc_method # visible at /rpc/
def add(a: int, b: int) -> int:
return a + b
5.3 Source code
Check out the source code and read README.md for more details on how to run and test:
https://gitlab.com/cuong-labs/jsonrpcdjangolab
6 Hardening for production
- Transport security – always HTTPS; if internal only, still require TLS within cluster.
- Authentication & authorisation – JWT or mutual‑TLS;
modernrpc
provides@rpc_method(auth="token")
. - Rate limiting & API gateway – NGINX, Kong, or Envoy in front; defend against method‑spam.
- Observability – log
method
,id
, latency, error code; expose Prometheus counters. - Timeouts & retries – client: connect‑timeout 2–3 s, total 5–10 s; exponential back‑off ensure idempotent operations or deduplicate by
id
. - Batch & notification – use batch for fan‑out calls; use notification for fire‑and‑forget (logging, metrics).
- When to upgrade to gRPC – need bi‑directional streaming, very high QPS, or strict IDL & code‑gen across many languages.
7 Wrap‑up
JSON‑RPC 2.0 hits a sweet spot for function‑centric micro‑services that prefer simplicity over the full REST verb set and don’t need gRPC’s binary speed or streaming.
- django‑modern‑rpc gives first‑class JSON‑RPC on Django 4/5 with docs & auth.
- tinyrpc excels when you mix Python frameworks or want ZeroMQ/RabbitMQ transport.