JSON-RPC 2.0 with Swagger UI
Tags: go, openapi, jsonrpc, swagger
Published: 2021-07-04 20:29:22

> JSON-RPC is a stateless, light-weight remote procedure call (RPC) protocol. Primarily this specification defines several data structures and the rules around their processing. It is transport agnostic in that the concepts can be used within the same process, over sockets, over http, or in many various message passing environments. It uses JSON (RFC 4627) as data format.

> It is designed to be simple!

https://www.jsonrpc.org/specification

There are cases when JSON-RPC might be a better fit than other technologies. Especially when API is consumed by legacy systems with high cost of change.

* Built-in semantics for batch requests offloads parallel processing to back end to optimize performance of slow synchronous clients.
* Uniform request/response structure makes it easy implement a generalized transport-level client that does not need to be updated on API extension.

Unfortunately, documenting JSON-RPC in machine-readable way lacks an established and popular standard (like [OpenAPI](https://swagger.io/) for REST). 

There is [OpenRPC](https://open-rpc.org/), which hopefully will grow its ecosystem and users base.

Another great tool to serve documentation and web client for REST APIs is [Swagger UI](https://swagger.io/tools/swagger-ui/). With a bit of tweaking/hacking we can reuse it for JSON-RPC 2.0.

`SwaggerUIBundle` constructor accepts `requestInterceptor` in configuration. Provided that JSON-RPC method is available as OpenAPI path item, this interceptor can reroute request and envelope it as JSON-RPC.

Mind `/rpc` in the following code, it is the path to JSON-RPC endpoint, and it may vary from one API to another.

```javascript
requestInterceptor: function(request) {
    if (request.loadSpec) {
        return request;
    }
    var url = window.location.protocol + '//'+ window.location.host;
    var method = request.url.substring(url.length);
    request.url = url + '/rpc';
    request.body = '{"jsonrpc": "2.0", "method": "' + method + '", "id": 1, "params": ' + request.body + '}';
    return request;
}
```

Now, if you want to integrate `Swagger UI` with your JSON-RPC API you need to create a fake OpenAPI schema with structures of your methods.

```json
{
  "openapi":"3.0.3",
  "info":{
    "title":"JSON-RPC Example",
    "description":"This app showcases a trivial JSON-RPC API.",
    "version":"v1.2.3"
  },
  "paths":{
    "nameLength":{
      "post":{
        "summary":"Test","description":"Test Description",
        "operationId":"nameLength",
        "requestBody":{
          "content":{
            "application/json":{"schema":{"$ref":"#/components/schemas/JsonrpcTestInp"}}
          }
        },
        "responses":{
          "200":{
            "description":"OK",
            "content":{
              "application/json":{"schema":{"$ref":"#/components/schemas/JsonrpcTestOut"}}
            }
          }
        }
      }
    }
  },
  "components":{
    "schemas":{
      "JsonrpcTestInp":{"type":"object","properties":{"name":{"type":"string"}}},
      "JsonrpcTestOut":{"type":"object","properties":{"len":{"type":"integer"}}}
    }
  },
  "x-envelope":"jsonrpc-2.0"
}
```

![Screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2iagd64lyctp4qmfm3vy.png)
 
This approach has been automated with a [`github.com/swaggest/jsonrpc`](https://github.com/swaggest/jsonrpc/) Go library that serves self-documented JSON-RPC from use case interactors. Support for `OpenRPC` might be implemented too.