skip to content

A JSON-RPC 2.0 server receives a batch array — what must it send back, and when is the reply a single object or nothing?

level: middleimportance: should knowfreq 15%

answer

  1. an Array in, usually an Array out
  2. order is not promised
  3. match by id, not position
  4. empty and all-notification edge cases

basics

~20 s

A JSON-RPC 2.0 batch is an Array of Request objects; the server answers with an Array of Responses, one per non-notification, in any order, matched by id. Invalid JSON or an empty Array gets one error object; an all-notification batch gets nothing.

solid answer

~50 s

A client MAY send several Request objects as one JSON Array. The server should answer, once all of them are processed, with an Array holding a Response for each Request — none for notifications. It MAY run them concurrently and in any order and MAY return the Responses in any order, so the client SHOULD match them by `id`, never by position. Three edge cases change the shape: unparseable JSON (-32700) or an empty Array (-32600) gets a single Response object with `id` null, not an Array; a non-empty Array of junk such as `[1,2,3]` gets an Array of -32600 errors, one per element; and a batch made only of notifications gets nothing at all, because the server MUST NOT return an empty Array. Nothing in the specification makes a batch atomic: each element succeeds or fails on its own.

code

json · 6 lines
json
[
  {"jsonrpc": "2.0", "method": "stock.reserve", "params": {"sku": "TB-204", "qty": 2}, "id": 11},
  {"jsonrpc": "2.0", "method": "audit.record", "params": ["reserve", "TB-204"]},
  {"jsonrpc": "2.0", "method": "stock.reserv", "params": {"sku": "TB-205", "qty": 1}, "id": 12},
  {"jsonrpc": "2.0", "params": [1]}
]

go deeper

for a junior

Recall that a batch is an Array of requests and that its reply is an Array of responses, one for each request that carries an id.

for a middle

Explain the edge cases precisely: unparseable JSON and an empty Array get a single object, junk elements get one error each, and an all-notification batch gets nothing.

for a senior

Explain why batch replies must be matched by id, why ordering and atomicity cannot be assumed, and how you would restructure a client that relied on either.

for a principal

Decide whether an API should encourage batching at all, weighing fewer round trips against the ordering and partial-failure semantics every client then has to handle correctly.

## What a batch is In JSON-RPC 2.0 a **batch** is a JSON Array filled with Request objects, sent in one go. The specification makes it optional for the Client (it MAY send one) and gives it no meaning beyond "several Request objects at the same time": there is no batch header, no batch id and no transactional wrapper. Every element is judged on its own as a call, a notification or an invalid object. ## The normal reply For a well-formed batch the rules are: - The Server **should respond with an Array** containing the corresponding Response objects, after all of the batch's Request objects have been processed. - A Response object **SHOULD exist for each Request object**, except that there SHOULD NOT be any Response objects for notifications. - The Server **MAY process the batch as a set of concurrent tasks**, in any order and with any width of parallelism. - The Responses **MAY be returned in any order** within the Array. - The Client **SHOULD match** Requests to Responses **by `id`**. Because the reply Array leaves out notifications, may reorder everything, and contains null-id entries for unreadable elements, its positions carry no meaning. A Client that pairs request 3 with reply 3 will eventually pair the wrong things. Matching by `id` also means the ids within one batch need to be distinct for the pairing to work. ## Edge cases that change the shape | What the Server receives | What it sends back | |---|---| | Text that is not valid JSON | One Response object: `-32700 Parse error`, `id` null | | An empty Array `[]` | One Response object: `-32600 Invalid Request`, `id` null | | `[1]` — non-empty, nothing valid | An Array with one `-32600` Response, `id` null | | `[1,2,3]` | An Array with three `-32600` Responses, each `id` null | | Only notifications | Nothing at all | The rule behind the first two rows: if the batch itself fails to be recognised as valid JSON or as an Array with at least one value, the reply MUST be a **single Response object**, not an Array. The rule behind the last row: if the reply Array would contain no Response objects, the Server **MUST NOT return an empty Array**, and the specification says it should return nothing at all. ## Order, concurrency and atomicity Two habits from other systems mislead here: 1. **Ordering.** Nothing obliges the Server to execute elements in Array order. A batch holding "create the record" followed by "update the record" may run the update first. A Client that needs sequencing must send the calls one after another and wait for each Response. 2. **All-or-nothing.** The specification gives every element its own Response and says nothing about undoing the others when one fails. If several steps must commit together, they belong in one method whose implementation is atomic on the Server, not in a batch of separate calls. What a batch does buy is fewer round trips: several calls share one message, and a Server that parallelises them may finish sooner than the same calls sent one by one. ## Reading a mixed batch Take this request: ```json [ {"jsonrpc": "2.0", "method": "stock.reserve", "params": {"sku": "TB-204", "qty": 2}, "id": 11}, {"jsonrpc": "2.0", "method": "audit.record", "params": ["reserve", "TB-204"]}, {"jsonrpc": "2.0", "method": "stock.reserv", "params": {"sku": "TB-205", "qty": 1}, "id": 12}, {"jsonrpc": "2.0", "params": [1]} ] ``` Assuming `stock.reserve` exists and succeeds, and `stock.reserv` does not exist: 1. Element 1 is a call: a Response with `result` and `id` 11. 2. Element 2 has no `id` and is otherwise valid: a notification, so no Response — even if recording fails. 3. Element 3 is a call to a missing method: a Response with `error` code `-32601` and `id` 12. 4. Element 4 has no `method`, so it is not a valid Request: a Response with `-32600` and `id` null. It lacks an `id`, but an invalid object is not a notification. The reply is an Array of three Response objects, in whatever order the Server chooses: ```json [ {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}, {"jsonrpc": "2.0", "result": {"held": 2}, "id": 11}, {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": 12} ] ``` ## When the batch rides on HTTP The 2.0 specification defines no HTTP mapping, so it does not say what HTTP status or body accompanies "nothing at all" for an all-notification batch. HTTP still pairs each request with a response, so whatever carries the batch has to choose — that is the carrying protocol's or the implementation's decision, not a JSON-RPC rule.

  • Why can't a JSON-RPC 2.0 client rely on a Response's position in the batch reply Array?
    The server MAY process the batch concurrently in any order and MAY return Responses in any order; notifications produce no entry at all; and unreadable elements produce entries whose id is null. Position therefore carries no meaning, and the client SHOULD match each Response to its Request by id — which also means ids inside one batch must be distinct for the matching to work.
  • Does a JSON-RPC 2.0 batch give the client all-or-nothing behaviour when one element fails?
    No. The specification defines a batch as several Request objects sent at once, each producing its own Response, and says nothing about undoing the others when one fails. If several steps must succeed or fail together, that needs a single method whose server-side implementation is atomic, not a batch of separate calls.

saying these in an interview costs you the question

  • Responses in a batch reply come back in the same order as the requests.
  • An empty batch array is answered with an empty array.
  • A batch made only of notifications is answered with an empty array.
  • If one call in a batch fails, the whole batch fails with one error.
  • The server must execute batch elements one after another, in order.