Skip to content

[Feature] Add request payload builders for outbound JSON-RPC composition #22

@limehee

Description

@limehee

Problem statement

Composing outbound JSON-RPC requests currently requires manual JsonNode or raw JSON assembly. This makes it easy to introduce structural mistakes around jsonrpc, id, notification vs request semantics, and params shape, while also repeating the same payload-building code across integrations.

The current JsonRpcRequest type is not the right user-facing composition target for this problem. It is primarily a parser output model used inside the dispatcher pipeline, with fields such as idPresent and source that encode parsing semantics rather than outbound payload authoring intent.

By contrast, JsonRpcResponse already exposes clear static factories for success and error responses, so response-side builders are not necessary for this issue.

Proposed solution

Add request payload builders in jsonrpc-core for outbound JSON-RPC composition:

  • JsonRpcRequestBuilder
  • JsonRpcRequestBatchBuilder

Design goals:

  • explicit request vs notification entry points
  • RFC-friendly default jsonrpc: "2.0"
  • fail-fast behavior for invalid builder states
  • transport-agnostic Jackson ObjectNode / ArrayNode output via buildNode()

Expected builder behavior:

  • request(method) requires an id before buildNode()
  • notification(method) must reject any id(...) call
  • batch builder must reject empty batches
  • params helpers should support both object and array composition without requiring raw JSON strings

Proposed API shape:

JsonRpcRequestBuilder.request("subtract")
JsonRpcRequestBuilder.notification("heartbeat")

JsonRpcRequestBuilder
    .id(long value)
    .id(String value)
    .id(JsonNode value)
    .nullId()
    .params(JsonNode value)
    .paramsArray(JsonNode... elements)
    .paramsObject(Consumer<ObjectNode> configurator)
    .buildNode()

JsonRpcRequestBatchBuilder
    .add(JsonRpcRequestBuilder builder)
    .addRequest(String method, Consumer<JsonRpcRequestBuilder> customizer)
    .addNotification(String method, Consumer<JsonRpcRequestBuilder> customizer)
    .buildNode()

Notes for the API contract:

  • JsonRpcRequestBuilder.request(...) must throw on buildNode() when no id has been set.
  • JsonRpcRequestBuilder.notification(...) must throw immediately when any id(...) or nullId() method is called.
  • calling params(...), paramsArray(...), or paramsObject(...) more than once on the same builder must throw immediately.
  • paramsObject(...) should create an ObjectNode internally and expose it to the configurator.
  • paramsArray(...) should create an ArrayNode from the provided elements.
  • buildNode() should return ObjectNode for single request/notification builders and ArrayNode for batch builders.

Also add a supporting convenience overload to JsonRpcError:

  • JsonRpcError.of(int code, String message, JsonNode data)

This covers the only missing response-side convenience currently identified, without introducing a separate response builder abstraction.

Alternatives considered

  • Keep the original broader scope with request and response builders
  • Expose buildModel() returning JsonRpcRequest
  • Continue using documentation-only snippets or user-defined helper classes

These approaches are less aligned with the current codebase design. JsonRpcRequest is a parser-oriented internal model, not an outbound composition API, and JsonRpcResponse already has adequate factory methods for normal response construction.

JSON-RPC behavior impact

No protocol behavior change. This adds outbound payload composition utilities and a small error-object factory convenience.

Additional context

  • Keep the implementation Spring-independent and limited to jsonrpc-core.
  • Build nodes directly with JsonNodeFactory.instance; do not require an ObjectMapper.
  • Update JsonRpcRequest Javadoc to clarify that it is primarily a parsed internal model.
  • Add unit tests for request/notification distinction, required-id behavior, notification id rejection, params composition, and empty-batch rejection.
  • Add documentation examples in README and pure Java guides for outbound request composition.
  • Response builders and response batch builders are intentionally out of scope for this issue.

Metadata

Metadata

Assignees

Labels

type: featureNew feature specification and implementation.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions