7.12.3. JSONRPC-03 — §6 Batches: Many Messages in One Wire

This tutorial covers JSON-RPC 2.0 §6 batch requests — sending and receiving multiple messages in a single wire payload. The semantics are subtle (empty array is an error, all-notifications batch yields nothing on the wire, per-entry errors are individual envelopes), but daslib/jsonrpc handles all the edge cases.

7.12.3.1. Mixed batch round-trip

A client batches two requests plus one notification. The server dispatches them. The response is an array of two entries: the notification produces no response and input order is preserved.

let entries <- [
    make_request("ping", "null", 1),
    make_notification("log", "\{\"msg\":\"hello\"}"),
    make_request("echo", "[42]", 2)
]
let wire = make_batch(entries)

let resp = jsonrpc::dispatch_line(wire, false) $(m, p) {
    if (m == "ping") return "\"pong\""
    if (m == "echo") return jsonrpc::compact_json_whitespace(p)
    return "\"unknown\""
}
// resp = [{"id":1,"result":"pong"},{"id":2,"result":[42]}]

7.12.3.2. Per-entry errors: continue-on-error

Malformed entries — missing method, bad id type, etc. — get individual error envelopes in their array slot. Valid entries still dispatch:

let wire = "[\{\"id\":1,\"method\":\"ping\"},\{\"id\":2},\{\"id\":3,\"method\":\"echo\",\"params\":[\"x\"]}]"
let resp = jsonrpc::dispatch_line(wire, false) $(m, p) { return "ok" }
// resp has three entries:
//   id=1 result
//   id=2 error -32600 (missing method)
//   id=3 result

7.12.3.3. Empty batch array

Per §6, an empty array [] is itself an invalid request. The server responds with a SINGLE error envelope, not an array:

let resp = jsonrpc::dispatch_line("[]", false) $(m, p) { return "" }
// resp = {"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"invalid request: empty batch"}}

7.12.3.4. All-notifications batch

If every entry in a batch is a notification, the server processes them (dispatcher runs for each) but emits nothing on the wire — the response array would be empty, so per §6 the server returns nothing at all:

let wire = "[\{\"method\":\"a\"},\{\"method\":\"b\"}]"
let resp = jsonrpc::dispatch_line(wire, false) $(m, p) { return "ignored" }
// resp == ""

7.12.3.5. Manual batch handling

For custom error semantics (per-method INVALID_PARAMS, METHOD_NOT_FOUND, logging per entry, etc.), use parse_batch directly:

let pb = parse_batch(wire)
if (!empty(pb.framing_error)) return pb.framing_error
for (req in pb.requests) {
    if (!empty(req.error_envelope)) { /* per-entry error */ }
    else { /* req.method, req.id_str, req.params, req.params_json available */ }
}

7.12.3.6. Running the tutorial

daslang.exe tutorials/jsonrpc/03_batch.das

Full source: tutorials/jsonrpc/03_batch.das

7.12.3.7. See also