Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Sending a JSON-RPC request

Before sending any request, you should have already established a connection.

Requests and notifications

Within the JSON-RPC specification, a client request can specify whether a reply from the server is required. If no reply is expected, the message is a notification. Because notifications do not generate responses, the client receives no confirmation that the server received the request, processed it successfully, encountered an error, or produced any output.

A notification should not be used just because a method does not return a value. Even when a method has no return data, using the standard request-response model ensures the client can detect failures or execution errors on the server side.

Argument arrays vs. an argument object

The JSON-RPC protocol passes arguments from client to server using either an array or as a single JSON object with a property for each parameter on the target method. Essentially, this leads to argument-to-parameter matching by position or by name.

Most JSON-RPC servers expect an array. json_rpc supports passing arguments both as a parameter object and in an array.

Invoking methods using compile-time definitions

The createRpcSigsFromNim macro accepts a list of forward procedure declarations and it generates the client RPCs. The RpcConv defined in the flavors section is used in the following example:

createRpcSigsFromNim(RpcClient, RpcConv):
  proc hello(input: string): string

Wrapping the method name in backticks allows any character:

proc `🙂`(input: string): string

The RPC method can be invoked using a client instance with a stablished connection:

let resp1 = await client.hello("Daisy")

The createRpcSigs macro accepts the path of a file containing a list of forward proc declarations and it generates the client RPCs of it:

const sigsFilePath = currentSourcePath().parentDir / "client_sigs.nim1"
createRpcSigs(RpcClient, sigsFilePath, RpcConv)

The createSingleRpcSig macro accepts a single forward proc declaration and an alias. The alias can be used to invoke the RPC method:

createSingleRpcSig(RpcClient, "sayBye", RpcConv):
  proc bye(input: string): string

The createRpcSigsFromString macro accepts a string containing a list of forward proc declarations and it generates the client RPCs:

const rpcClientDefs = staticRead(sigsFilePath)
createRpcSigsFromString(RpcClient, rpcClientDefs, RpcConv)

Invoking methods using runtime information

An RPC method can be invoked passing its name an parameter types at runtime. The parameter must be passed as a JsonNode or RequestParamsTx. The RpcConv defined in the flavors section is used in the following example:

let resp2 = await client.call("hello", %* ["Daisy"], RpcConv)

Using named parameters is allowed. Some server implementations may support only positional parameters, json_rpc supports both styles:

let resp3 = await client.call("hello", %* {"input": "Daisy"}, RpcConv)

When the method doesn't take parameters, it can be invoked passing an empty array:

let resp4 = await client.call("justHello", %* [], RpcConv)

The response from call is a JsonString which can be decoded using json_serialization:

doAssert RpcConv.decode(resp2, string) == "Hello Daisy"

Sending batch requests

The JSON-RPC specification allows for batching requests and getting a response containing an array of responses for each request.

The prepareBatch client function can be used to batch requests and send them all at once:

let batch = client.prepareBatch()
batch.hello("Daisy")
batch.`🙂`("Daisy")
let batchRes = await batch.send()

The send return value is an optional result with either the sequence of RPC responses, or an error indicating there was an error processing the array of responses. Each response contains a result or an error. The result field is the JSON encoded RPC result. If the optional error field is set, it'll contain either an error message or the JSON encoded RPC error response:

let r = batchRes.tryGet()
doAssert r[0].error.isNone
doAssert RpcConv.decode(r[0].result, string) == "Hello Daisy"
doAssert r[1].error.isNone
doAssert RpcConv.decode(r[1].result, string) == "🙂 Daisy"

Sending a notification

A notification can be sent for fire and forget method invocations. As mentioned earlier, the method response is not returned, and the client is not notified about server errors:

await client.notify("empty", RequestParamsTx())

Exception handling

RPC methods may throw exceptions. The RPC client should be prepared to handle these exceptions.

Learn more about throwing and handling exceptions.