{"id":13412601,"url":"https://github.com/ybbus/jsonrpc","last_synced_at":"2025-03-14T18:31:49.384Z","repository":{"id":13294219,"uuid":"73375722","full_name":"ybbus/jsonrpc","owner":"ybbus","description":"A simple go implementation of json rpc 2.0 client over http","archived":false,"fork":false,"pushed_at":"2024-08-08T12:46:34.000Z","size":113,"stargazers_count":326,"open_issues_count":1,"forks_count":92,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-10-01T08:41:24.354Z","etag":null,"topics":["golang","json-rpc"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ybbus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-11-10T11:27:55.000Z","updated_at":"2024-09-25T10:50:53.000Z","dependencies_parsed_at":"2024-06-18T12:29:30.447Z","dependency_job_id":"1fcda541-a467-4c3d-bce7-ec741e0bd36f","html_url":"https://github.com/ybbus/jsonrpc","commit_stats":null,"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ybbus%2Fjsonrpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ybbus%2Fjsonrpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ybbus%2Fjsonrpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ybbus%2Fjsonrpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ybbus","download_url":"https://codeload.github.com/ybbus/jsonrpc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243625152,"owners_count":20321241,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","json-rpc"],"created_at":"2024-07-30T20:01:26.659Z","updated_at":"2025-03-14T18:31:49.091Z","avatar_url":"https://github.com/ybbus.png","language":"Go","readme":"[![Go Report Card](https://goreportcard.com/badge/github.com/ybbus/jsonrpc/v3)](https://goreportcard.com/report/github.com/ybbus/jsonrpc/v3)\n[![Go build](https://github.com/ybbus/jsonrpc/actions/workflows/go.yml/badge.svg)](https://github.com/ybbus/jsonrpc)\n[![Codecov](https://codecov.io/github/ybbus/jsonrpc/branch/master/graph/badge.svg?token=ARYOQ8R1DT)](https://codecov.io/github/ybbus/jsonrpc)\n[![GoDoc](https://godoc.org/github.com/ybbus/jsonrpc/v3?status.svg)](https://godoc.org/github.com/ybbus/jsonrpc/v3)\n[![GitHub license](https://img.shields.io/github/license/mashape/apistatus.svg)]()\n[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)  \n\n# JSON-RPC 2.0 Client for golang\nA go implementation of an rpc client using json as data format over http.\nThe implementation is based on the JSON-RPC 2.0 specification: http://www.jsonrpc.org/specification\n\nSupports:\n- requests with arbitrary parameters\n- convenient response retrieval\n- batch requests\n- custom http client (e.g. proxy, tls config)\n- custom headers (e.g. basic auth)\n\n## Installation\n\n```sh\ngo get -u github.com/ybbus/jsonrpc/v3\n```\n\n(You can find v2 and v1 in a separate branch.)\n\n## Getting started\nLet's say we want to retrieve a person struct with a specific id using rpc-json over http.\nThen we want to save this person after we changed a property.\n(Error handling is omitted here)\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"github.com/ybbus/jsonrpc/v3\"\n)\n\ntype Person struct {\n\tID   int    `json:\"id\"`\n\tName string `json:\"name\"`\n\tAge  int    `json:\"age\"`\n}\n\nfunc main() {\n\trpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n\n\tvar person *Person\n\trpcClient.CallFor(context.Background(), \u0026person, \"getPersonById\", 4711)\n\n\tperson.Age = 33\n\trpcClient.Call(context.Background(), \"updatePerson\", person)\n}\n\n```\n\n## In detail\n\n### Generating rpc-json requests\n\nLet's start by executing a simple json-rpc http call:\nIn production code: Always make sure to check err != nil first!\n\nThis calls generate and send a valid rpc-json object. (see: http://www.jsonrpc.org/specification#request_object)\n\n```go\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n    rpcClient.Call(ctx, \"getDate\")\n    // generates body: {\"method\":\"getDate\",\"id\":0,\"jsonrpc\":\"2.0\"}\n}\n```\n\nCall a function with parameter:\n\n```go\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n    rpcClient.Call(ctx, \"addNumbers\", 1, 2)\n    // generates body: {\"method\":\"addNumbers\",\"params\":[1,2],\"id\":0,\"jsonrpc\":\"2.0\"}\n}\n```\n\nCall a function with arbitrary parameters:\n\n```go\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n    rpcClient.Call(ctx, \"createPerson\", \"Alex\", 33, \"Germany\")\n    // generates body: {\"method\":\"createPerson\",\"params\":[\"Alex\",33,\"Germany\"],\"id\":0,\"jsonrpc\":\"2.0\"}\n}\n```\n\nCall a function providing custom data structures as parameters:\n\n```go\ntype Person struct {\n  Name    string `json:\"name\"`\n  Age     int `json:\"age\"`\n  Country string `json:\"country\"`\n}\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n    rpcClient.Call(ctx, \"createPerson\", \u0026Person{\"Alex\", 33, \"Germany\"})\n    // generates body: {\"jsonrpc\":\"2.0\",\"method\":\"createPerson\",\"params\":{\"name\":\"Alex\",\"age\":33,\"country\":\"Germany\"},\"id\":0}\n}\n```\n\nComplex example:\n\n```go\ntype Person struct {\n  Name    string `json:\"name\"`\n  Age     int `json:\"age\"`\n  Country string `json:\"country\"`\n}\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n    rpcClient.Call(ctx, \"createPersonsWithRole\", \u0026Person{\"Alex\", 33, \"Germany\"}, \u0026Person{\"Barney\", 38, \"Germany\"}, []string{\"Admin\", \"User\"})\n    // generates body: {\"jsonrpc\":\"2.0\",\"method\":\"createPersonsWithRole\",\"params\":[{\"name\":\"Alex\",\"age\":33,\"country\":\"Germany\"},{\"name\":\"Barney\",\"age\":38,\"country\":\"Germany\"},[\"Admin\",\"User\"]],\"id\":0}\n}\n```\n\nSome examples and resulting JSON-RPC objects:\n\n```go\nrpcClient.Call(ctx, \"missingParam\")\n{\"method\":\"missingParam\"}\n\nrpcClient.Call(ctx, \"nullParam\", nil)\n{\"method\":\"nullParam\",\"params\":[null]}\n\nrpcClient.Call(ctx, \"boolParam\", true)\n{\"method\":\"boolParam\",\"params\":[true]}\n\nrpcClient.Call(ctx, \"boolParams\", true, false, true)\n{\"method\":\"boolParams\",\"params\":[true,false,true]}\n\nrpcClient.Call(ctx, \"stringParam\", \"Alex\")\n{\"method\":\"stringParam\",\"params\":[\"Alex\"]}\n\nrpcClient.Call(ctx, \"stringParams\", \"JSON\", \"RPC\")\n{\"method\":\"stringParams\",\"params\":[\"JSON\",\"RPC\"]}\n\nrpcClient.Call(ctx, \"numberParam\", 123)\n{\"method\":\"numberParam\",\"params\":[123]}\n\nrpcClient.Call(ctx, \"numberParams\", 123, 321)\n{\"method\":\"numberParams\",\"params\":[123,321]}\n\nrpcClient.Call(ctx, \"floatParam\", 1.23)\n{\"method\":\"floatParam\",\"params\":[1.23]}\n\nrpcClient.Call(ctx, \"floatParams\", 1.23, 3.21)\n{\"method\":\"floatParams\",\"params\":[1.23,3.21]}\n\nrpcClient.Call(ctx, \"manyParams\", \"Alex\", 35, true, nil, 2.34)\n{\"method\":\"manyParams\",\"params\":[\"Alex\",35,true,null,2.34]}\n\nrpcClient.Call(ctx, \"singlePointerToStruct\", \u0026person)\n{\"method\":\"singlePointerToStruct\",\"params\":{\"name\":\"Alex\",\"age\":35,\"country\":\"Germany\"}}\n\nrpcClient.Call(ctx, \"multipleStructs\", \u0026person, \u0026drink)\n{\"method\":\"multipleStructs\",\"params\":[{\"name\":\"Alex\",\"age\":35,\"country\":\"Germany\"},{\"name\":\"Cuba Libre\",\"ingredients\":[\"rum\",\"cola\"]}]}\n\nrpcClient.Call(ctx, \"singleStructInArray\", []*Person{\u0026person})\n{\"method\":\"singleStructInArray\",\"params\":[{\"name\":\"Alex\",\"age\":35,\"country\":\"Germany\"}]}\n\nrpcClient.Call(ctx, \"namedParameters\", map[string]interface{}{\n\t\"name\": \"Alex\",\n\t\"age\":  35,\n})\n{\"method\":\"namedParameters\",\"params\":{\"age\":35,\"name\":\"Alex\"}}\n\nrpcClient.Call(ctx, \"anonymousStruct\", struct {\n\tName string `json:\"name\"`\n\tAge  int    `json:\"age\"`\n}{\"Alex\", 33})\n{\"method\":\"anonymousStructWithTags\",\"params\":{\"name\":\"Alex\",\"age\":33}}\n\nrpcClient.Call(ctx, \"structWithNullField\", struct {\n\tName    string  `json:\"name\"`\n\tAddress *string `json:\"address\"`\n}{\"Alex\", nil})\n{\"method\":\"structWithNullField\",\"params\":{\"name\":\"Alex\",\"address\":null}}\n```\n\n### Working with rpc-json responses\n\n\nBefore working with the response object, make sure to check err != nil.\nAlso keep in mind that the json-rpc result field can be nil even on success.\n\n```go\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n    response, err := rpcClient.Call(ctx, \"addNumbers\", 1, 2)\n    if err != nil {\n      // error handling goes here e.g. network / http error\n    }\n}\n```\n\nIf an http error occurred, maybe you are interested in the error code (403 etc.)\n```go\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n    response, err := rpcClient.Call(ctx, \"addNumbers\", 1, 2)\n\n    switch e := err.(type) {\n      case nil: // if error is nil, do nothing\n      case *HTTPError:\n        // use e.Code here\n        return\n      default:\n        // any other error\n        return\n    }\n\n    // no error, go on...\n}\n```\n\nThe next thing you have to check is if an rpc-json protocol error occurred. This is done by checking if the Error field in the rpc-response != nil:\n(see: http://www.jsonrpc.org/specification#error_object)\n\n```go\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n    response, err := rpcClient.Call(ctx, \"addNumbers\", 1, 2)\n    if err != nil {\n        //error handling goes here\n    }\n\n    if response.Error != nil {\n        // rpc error handling goes here\n        // check response.Error.Code, response.Error.Message and optional response.Error.Data\n    }\n}\n```\n\nAfter making sure that no errors occurred you can now examine the RPCResponse object.\nWhen executing a json-rpc request, most of the time you will be interested in the \"result\"-property of the returned json-rpc response object.\n(see: http://www.jsonrpc.org/specification#response_object)\nThe library provides some helper functions to retrieve the result in the data format you are interested in.\nAgain: check for err != nil here to be sure the expected type was provided in the response and could be parsed.\n\n```go\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n    response, _ := rpcClient.Call(ctx, \"addNumbers\", 1, 2)\n\n    result, err := response.GetInt()\n    if err != nil {\n        // result cannot be unmarshalled as integer\n    }\n\n    // helpers provided for all primitive types:\n    response.GetInt()\n    response.GetFloat()\n    response.GetString()\n    response.GetBool()\n}\n```\n\nRetrieving arrays and objects is also very simple:\n\n```go\n// json annotations are only required to transform the structure back to json\ntype Person struct {\n    Id   int `json:\"id\"`\n    Name string `json:\"name\"`\n    Age  int `json:\"age\"`\n}\n\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n    response, _ := rpcClient.Call(ctx, \"getPersonById\", 123)\n\n    var person *Person\n    err := response.GetObject(\u0026person) // expects a rpc-object result value like: {\"id\": 123, \"name\": \"alex\", \"age\": 33}\n    if err != nil || person == nil {\n        // some error on json unmarshal level or json result field was null\n    }\n\n    fmt.Println(person.Name)\n\n    // we can also set default values if they are missing from the result, or result == null:\n    person2 := \u0026Person{\n        Id: 0,\n        Name: \"\u003cempty\u003e\",\n        Age: -1,\n    }\n    err := response.GetObject(\u0026person2) // expects a rpc-object result value like: {\"id\": 123, \"name\": \"alex\", \"age\": 33}\n    if err != nil || person2 == nil {\n        // some error on json unmarshal level or json result field was null\n    }\n\n    fmt.Println(person2.Name) // prints \"\u003cempty\u003e\" if \"name\" field was missing in result-json\n}\n```\n\nRetrieving arrays:\n\n```go\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n    response, _ := rpcClient.Call(ctx, \"getRandomNumbers\", 10)\n\n    rndNumbers := []int{}\n    err := response.GetObject(\u0026rndNumbers) // expects a rpc-object result value like: [10, 188, 14, 3]\n    if err != nil {\n        // do error handling\n    }\n\n    for _, num := range rndNumbers {\n        fmt.Printf(\"%v\\n\", num)\n    }\n}\n```\n\n### Using convenient function CallFor()\nA very handy way to quickly invoke methods and retrieve results is by using CallFor()\n\nYou can directly provide an object where the result should be stored. Be sure to provide it be reference.\nAn error is returned if:\n- there was an network / http error\n- RPCError object is not nil (err can be casted to this object)\n- rpc result could not be parsed into provided object\n\nOne of the above examples could look like this:\n\n```go\n// json annotations are only required to transform the structure back to json\ntype Person struct {\n    Id   int `json:\"id\"`\n    Name string `json:\"name\"`\n    Age  int `json:\"age\"`\n}\n\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n\n    var person *Person\n    err := rpcClient.CallFor(ctx, \u0026person, \"getPersonById\", 123)\n\n    if err != nil || person == nil {\n      // handle error\n    }\n\n    fmt.Println(person.Name)\n}\n```\n\nMost of the time it is ok to check if a struct field is 0, empty string \"\" etc. to check if it was provided by the json rpc response.\nBut if you want to be sure that a JSON-RPC response field was missing or not, you should use pointers to the fields.\nThis is just a single example since all this Unmarshaling is standard go json functionality, exactly as if you would call json.Unmarshal(rpcResponse.ResultAsByteArray, \u0026objectToStoreResult)\n\n```go\ntype Person struct {\n    Id   *int    `json:\"id\"`\n    Name *string `json:\"name\"`\n    Age  *int    `json:\"age\"`\n}\n\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n\n    var person *Person\n    err := rpcClient.CallFor(ctx, \u0026person, \"getPersonById\", 123)\n\n    if err != nil || person == nil {\n      // handle error\n    }\n\n    if person.Name == nil {\n      // json rpc response did not provide a field \"name\" in the result object\n    }\n}\n```\n\n### Using RPC Batch Requests\n\nYou can send multiple RPC-Requests in one single HTTP request using RPC Batch Requests.\n\n```go\nfunc main() {\n    rpcClient := jsonrpc.NewClient(\"http://my-rpc-service:8080/rpc\")\n\n    response, _ := rpcClient.CallBatch(ctx, RPCRequests{\n      NewRequest(\"myMethod1\", 1, 2, 3),\n      NewRequest(\"anotherMethod\", \"Alex\", 35, true),\n      NewRequest(\"myMethod2\", \u0026Person{\n        Name: \"Emmy\",\n        Age: 4,\n      }),\n    })\n}\n```\n\nKeep the following in mind:\n- the request / response id's are important to map the requests to the responses. CallBatch() automatically sets the ids to requests[i].ID == i\n- the response can be provided in an unordered and maybe incomplete form\n- when you want to set the id yourself use, CallRaw()\n\nThere are some helper methods for batch request results:\n```go\nfunc main() {\n    // [...]\n\n    result.HasErrors() // returns true if one of the rpc response objects has Error field != nil\n    resultMap := result.AsMap() // returns a map for easier retrieval of requests\n\n    if response123, ok := resultMap[123]; ok {\n      // response object with id 123 exists, use it here\n      // response123.ID == 123\n      response123.GetObjectAs(\u0026person)\n      // ...\n    }\n\n}\n```\n\n### Raw functions\nThere are also Raw function calls. Consider the non Raw functions first, unless you know what you are doing.\nYou can create invalid json rpc requests and have to take care of id's etc. yourself.\nAlso check documentation of Params() for raw requests.\n\n### Custom Headers, Basic authentication\n\nIf the rpc-service is running behind a basic authentication you can easily set the Authorization header:\n\n```go\nfunc main() {\n    rpcClient := jsonrpc.NewClientWithOpts(\"http://my-rpc-service:8080/rpc\", \u0026jsonrpc.RPCClientOpts{\n   \t\tCustomHeaders: map[string]string{\n   \t\t\t\"Authorization\": \"Basic \" + base64.StdEncoding.EncodeToString([]byte(\"myUser\"+\":\"+\"mySecret\")),\n   \t\t},\n   \t})\n    response, _ := rpcClient.Call(ctx, \"addNumbers\", 1, 2) // send with Authorization-Header\n}\n```\n\n### Using oauth\n\nUsing oauth is also easy, e.g. with clientID and clientSecret authentication\n\n```go\nfunc main() {\n\t\tcredentials := clientcredentials.Config{\n    \t\tClientID:     \"myID\",\n    \t\tClientSecret: \"mySecret\",\n    \t\tTokenURL:     \"http://mytokenurl\",\n    \t}\n\n    \trpcClient := jsonrpc.NewClientWithOpts(\"http://my-rpc-service:8080/rpc\", \u0026jsonrpc.RPCClientOpts{\n    \t\tHTTPClient: credentials.Client(context.Background()),\n    \t})\n\n\t// requests now retrieve and use an oauth token\n}\n```\n\n### Set a custom httpClient\n\nIf you have some special needs on the http.Client of the standard go library, just provide your own one.\nFor example to use a proxy when executing json-rpc calls:\n\n```go\nfunc main() {\n\tproxyURL, _ := url.Parse(\"http://proxy:8080\")\n\ttransport := \u0026http.Transport{Proxy: http.ProxyURL(proxyURL)}\n\n\thttpClient := \u0026http.Client{\n\t\tTransport: transport,\n\t}\n\n\trpcClient := jsonrpc.NewClientWithOpts(\"http://my-rpc-service:8080/rpc\", \u0026jsonrpc.RPCClientOpts{\n\t\tHTTPClient: httpClient,\n\t})\n\n\t// requests now use proxy\n}\n```\n\n### Allow unknown fields in json-rpc response object\n\nBy default, the client will return an error, if the response object contains fields, that are not defined in the response struct.\nYou may change this behavior by setting the RPCClientOpts.AllowUnknownFields to true:\n\n```go\nfunc main() {\n\trpcClient := jsonrpc.NewClientWithOpts(\"http://my-rpc-service:8080/rpc\", \u0026jsonrpc.RPCClientOpts{\n        AllowUnknownFields: true,\n\t})\n\n\t// unknown fields are now allowed in the response\n}\t\n```\n\n### Change default RPCRequestID\n\nBy default, the client will set the id of an RPCRequest to 0.\nThis can be changed by setting the RPCClientOpts.DefaultRequestID to a custom value:\n\n```go\nfunc main() {\n    rpcClient := jsonrpc.NewClientWithOpts(\"http://my-rpc-service:8080/rpc\", \u0026jsonrpc.RPCClientOpts{\n        DefaultRequestID: 1,\n    })\n\n    // requests now have default id 1\n}\t\n```\n\nYou may also use NewRequestWithID() to set a custom id when creating a raw request.\n","funding_links":[],"categories":["Distributed Systems","Relational Databases","Tool-kits \u0026 helpers","分布式系统","Go","分佈式系統","\u003cspan id=\"分布式系统-distributed-systems\"\u003e分布式系统 Distributed Systems\u003c/span\u003e"],"sub_categories":["Advanced Console UIs","Search and Analytic Databases","检索及分析资料库","SQL 查询语句构建库","高級控制台界面","\u003cspan id=\"高级控制台用户界面-advanced-console-uis\"\u003e高级控制台用户界面 Advanced Console UIs\u003c/span\u003e","高级控制台界面"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fybbus%2Fjsonrpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fybbus%2Fjsonrpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fybbus%2Fjsonrpc/lists"}