11JSON-RPC 2.0 for Erlang
22======================= 
33
4- Transport agnostic library for JSON-RPC 2.0 servers and clients.
4+ Library for JSON-RPC 2.0 using JSON library (EEP 68) from stdlib, logger (OTP 21.0) and Maps (EEP 43).
5+ 
6+ 
7+ Dependencies
8+ ------------ 
9+ 
10+ *  Erlang/OTP 27.0+
511
6- This page contains the manual for the server part, the ` jsonrpc2 `  module. The client part has a
7- separate module ` jsonrpc2_client ` . Client docs are yet to be written. For documentation on the
8- client library, see the source code: [ jsonrpc2_client.erl] ( src/jsonrpc2_client.erl ) .
912
1013Features
1114-------- 
1215
13- *  can use any JSON encoder and decoder that supports the eep0018 style terms
14-   format,
15- *  transport neutral
16- *  dispatches parsed requests to a simple callback function
16+ *  transport neutral;
17+ *  dispatches parsed requests to a simple callback function;
1718*  supports an optional callback "map" function for batch requests, e.g. to
18-   support concurrent processing of the requests in a batch, 
19- *  handles rpc calls and notifications, 
20- *  supports named and unnamed parameters, 
19+   support concurrent processing of the requests in a batch or collecting statistics; 
20+ *  handles rpc calls and notifications; 
21+ *  supports named and unnamed parameters; 
2122*  includes unit tests for all examples in the JSON-RPC 2.0 specification.
2223
23- Example 
24+ Server examples 
2425------- 
2526
26- ```  erlang 
27- 1 >  Json  =  <<" {\" jsonrpc\" : \" 2.0\" , \" method\" : \" foo\" , \" params\" : [1,2,3], \" id\" : 1}" 
28- <<" {\" jsonroc\" : \" 2.0\" , \" method\" : \" foo\" , \" params\" : [1,2,3], \" id\" : 1}" 
29- 2 > 
30- 2 >  MyHandler  =  fun  (<<" foo" Params ) -> lists :reverse (Params );
31- 2 >                  (_ , _ ) -> throw (method_not_found )
32- 2 >              end .
33- #Fun < erl_eval .12.82930912 > 
34- 3 > 
35- 3 >  jsonrpc2 :handle (Json , MyHandler , fun  jiffy :decode /1 , fun  jiffy :encode /1 ).
36- {reply ,<<" {\" jsonrpc\" :\" 2.0\" ,\" result\" :[3,2,1],\" id\" :1}" 
37- 4 > 
38- 4 >  jsonrpc2 :handle (<<" dummy" MyHandler , fun  jiffy :decode /1 , fun  jiffy :encode /1 ).
39- {reply ,<<" {\" jsonrpc\" :\" 2.0\" ,\" error\" :{\" code\" :-32700,\" message\" :\" Parse error.\" },\" id\" :null}" 
40- 5 > 
41- 5 >  jsonrpc2 :handle (<<" {\" x\" :42}" MyHandler , fun  jiffy :decode /1 , fun  jiffy :encode /1 ).
42- {reply ,<<" {\" jsonrpc\" :\" 2.0\" ,\" error\" :{\" code\" :-32600,\" message\" :\" Invalid Request.\" },\" id\" :null}" 
43- ``` 
44- 
45- Types
46- ----- 
47- 
48- ``` Erlang 
49- json () ::  true  | false  | null  | binary () | [json ()] | {[{binary (), json ()}]}.
50- 
51- handlerfun () ::  fun ((method (), params ()) ->  json ()).
52- method () ::  binary ().
53- params () ::  [json ()] | {[{binary (), json ()}]}.
54- 
55- mapfun () ::  fun ((fun ((A ) ->  B ), [A ]) ->  [B ]). % % the same as lists:map/2
56- ``` 
57- 
58- Functions
59- --------- 
27+ ``` erlang 
28+ 1 >  Body  =  ~ ' {"jsonrpc": "2.0", "method": "foo", "params": [1,2,3], "id": 1}' 
29+ <<" {\" jsonrpc\" : \" 2.0\" , \" method\" : \" foo\" , \" params\" : [1,2,3], \" id\" : 1}" 
6030
61- Any of the ` jsonrpc2:handle/2,3,4,5 `  functions can be used to handle JSON-RPC
62- request by delegating the actual procedure call to a handler callback function.
63- They all return ` {reply, Data} `  where Data is a result or an error response or
64- ` noreply `  when no response should be sent to the client. The handler callback
65- function must return a term that can be encoded to JSON using the
66- representation explained on the page https://github.com/davisp/jiffy#data-format ,
67- as required by jiffy and other compatible JSON parses.
31+ 2 >  Handler  =  fun (~ " foo" Params ) -> {ok , lists :reverse (Params )}; (_ , _ ) -> {error , method_not_found } end .
32+ #Fun < erl_eval .41.81571850 > 
6833
69- ``` Erlang 
70- handle (json (), handlerfun ()) ->  {reply , json ()} | noreply 
71- ```
34+ 3 >  jsonrpc2 :handle (Body , Handler ).
35+ {reply ,<<" {\" id\" :1,\" result\" :[3,2,1],\" jsonrpc\" :\" 2.0\" }" 
7236
73- Handles  decoded  JSON  and  returns  a  reply  as  decoded  JSON  or  noreply . Use 
74- this  if  you  want  to  handle  JSON  encoding  separately .
37+ 4 >  jsonrpc2 :handle (~ " dummy" Handler ).
38+ = WARNING  REPORT ====  10 - Aug - 2025 :: 13 :58 :12.336132  === 
39+ Failed  to  decode  request  in  JSON  format : error  {invalid_byte ,100 }
40+ {reply ,<<" {\" error\" :{\" code\" :-32700,\" message\" :\" Parse error.\" },\" id\" :null,\" jsonrpc\" :\" 2.0\" }" 
7541
76- ```Erlang 
77- handle (json (), handlerfun (), mapfun ()) -> {reply , json ()} | noreply 
78- ```
42+ 5 >  jsonrpc2 :handle (~ ' {"x": 42}' Handler ).
43+ {reply ,<<" {\" error\" :{\" code\" :-32600,\" message\" :\" Invalid Request.\" },\" id\" :null,\" jsonrpc\" :\" 2.0\" }" 
7944
80- Like  `handle / 2 `, handles  decoded  JSON , but  takes  an  extra 
81- " map" function  callback  to  be  used  instead  of  `lists :map / 2 `
82- for  batch  processing . The  map  function  should  be  a  function  that  behaves 
83- similarly  to  `lists :map / 2 `, such  as  the  `plists :map / 2 `
84- from  the  plists  library  for  concurrent  batch  handling .
8545
86- ``` Erlang 
87- handle ( Req :: term (),  handlerfun (),  JsonDecode :: fun (),  JsonEncode :: fun ()) -> 
88-     { reply ,  term ()} |  noreply 
89- ``` 
46+ 6 >   Term   =  #{ ~ " jsonrpc "   =>   ~ " 2.0 " ,  ~ " method "   =>   ~ " foo " ,  ~ " params "   =>  [ 1 , 2 , 3 ],  ~ " id "   =>   1 }. 
47+ #{<< " id " >>  =>   1 ,<< " jsonrpc " >>  =>  << " 2.0 " >>, 
48+   << " method " >>  =>  << " foo " >>, 
49+   << " params " >>  =>  [ 1 , 2 , 3 ]} 
9050
91- Handles   JSON   as   binary   or   string .  Uses   the   supplied   functions 
92- JsonDecode   to   parse   the   JSON   request   and   JsonEncode   to   encode   the   reply   as   JSON . 
51+ 7 >   jsonrpc2 : handle_term ( Term ,  Handler ). 
52+ { reply ,#{ id   =>   1 , result   =>  [ 3 , 2 , 1 ], jsonrpc   =>  << " 2.0 " >>}} 
9353
94- ```Erlang 
95- handle (Req :: term (), handlerfun (), mapfun (), JsonDecode :: fun (),
96-     JsonEncode :: fun ()) -> {reply , term ()} | noreply 
9754``` 
9855
99- Like  `handle / 4 `, but  also  takes  a  map  function  for  batch 
100- processing . See  `handle / 3 ` above .
10156
102- Error   Handling 
57+ Client example 
10358-------------- 
10459
105- A  requests  that  is  not  valid  JSON  results  in  a  " Parse error" JSON - RPC  response .
106- 
107- An  invalid  JSON - RPC  request  (though  valid  JSON ) results  in  an  " Invalid Request" 
108- response . In  these  two  cases  the  handler  callback  function  is  never  called .
109- 
110- To  produce  an  error  response  from  the  handler  function , you  may  throw  one  of 
111- the  exceptions  below . They  will  be  caught  and  turned  into  a  corresponding 
112- JSON - RPC  error  response .
113- 
114-   *  `throw (method_not_found )` is  reported  as  " Method not found" - 32601 )
115-   *  `throw (invalid_params )` is  reported  as  " Invalid params" - 32602 )
116-   *  `throw (internal_error )` is  reported  as  " Internal error" - 32603 )
117-   *  `throw (server_error )` is  reported  as  " Server error" - 32000 )
118- 
119- If  you  also  want  to  include  `data ` in  the  JSON - RPC  error  response , throw  a  pair 
120- with  the  error  type  and  the  data , such  as  `{internal_error , Data }`.
121- 
122- For  your  own  * application - defined  errors * , it  is  possible  to  set  a  custom  error 
123- code  by  throwing  a  tuple  with  the  atom  `jsonrpc2 `, an  integer  error  code , a 
124- binary  message  and  optional  data .
12560
126-   *  `throw ({jsonrpc2 , Code , Message )`
127-   *  `throw ({jsonrpc2 , Code , Message , Data })`
128- 
129- If  any  other  exception  is  thrown  or  an  error  occurs  in  the  handler , this  is 
130- caught , an  error  message  is  logged  (using  the  standard  error  logger 
131- `error_logger :error_msg / 2 `) and  an  " Internal error" response  is  returned .
61+ ``` erlang 
62+ 1 >  RequestsSpec  =  [
63+         #{method  =>  add , params  =>  [7000 , - 77 ]},
64+         #{method  =>  add , params  =>  [- 1 , 900 ], id  =>  0 }
65+     ].
66+ [#{params  =>  [7000 ,- 77 ],method  =>  add },
67+  #{id  =>  0 ,params  =>  [- 1 ,900 ],method  =>  add }]
13268
133- If  you 're working with already parsed JSON, i.e. you' re  using  `handle / 2 ` or 
134- `handle / 3 `, you  may  want  to  produce  an  error  message  that  you  can  use  when  the 
135- client  sends  invalid  JSON  that  can 't be parsed. Use `jsonrpc2:parseerror()` to 
136- create the appropriate error response for this purpose. 
69+ 2 >  TransportFn  =  fun (Body ) -> {ok , {_ ,_ ,Resp }} =  httpc :request (post , {" http://localhost:8080/rpc" " Content-Length" integer_to_binary (iolist_size (Body ))}], " application/json" Body }, [], [{body_format , binary }]), Resp  end .
70+ #Fun < erl_eval .42.81571850 > 
13771
138- Examples: 
72+ jsonrpc2_client :multi_call (RequestsSpec , TransportFn , 1 ).
73+ [{ok ,899 ,undefined }]
13974
140- ```erlang 
141- my_handler(<<"Foo">>, [X, Y]) when is_integer(X), is_integer(Y) -> 
142-     {[{<<"Foo says">>}, X + Y + 42}]}; 
143- my_handler(<<"Foo">>, _SomeOtherParams) -> 
144-     throw(invalid_params); 
145- my_handler(<<"Logout">>, [Username]) -> 
146-     throw({jsonrpc2, 123, <<"Not logged in">>}); 
147- my_handler(_SomeOtherMethod, _) -> 
148-     throw(method_not_found). 
14975``` 
15076
151- Compatible JSON parsers 
152- ----------------------- 
153- 
154- * Jiffy, https://github.com/davisp/jiffy 
155- * erlang-json, https://github.com/hio/erlang-json 
156- * Mochijson2 using ```mochijson2:decode(Bin, [{format, eep18}])``` 
157- * Probably more... 
15877
15978Links
16079----- 
@@ -163,11 +82,13 @@ Links
16382*  rjsonrpc2, a "restricted" implementation of JSON-RPC 2.0, https://github.com/imprest/rjsonrpc2 
16483*  ejrpc2, another JSON-RPC 2 library, https://github.com/jvliwanag/ejrpc2 
16584
85+ 
16686License
16787------- 
16888
16989``` 
17090Copyright 2013-2014 Viktor Söderqvist 
91+ Copyright 2025 Andy (https://github.com/m-2k) 
17192
17293Licensed under the Apache License, Version 2.0 (the "License"); 
17394you may not use this file except in compliance with the License. 
@@ -181,15 +102,3 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
181102See the License for the specific language governing permissions and 
182103limitations under the License. 
183104``` 
184- 
185- **Author' s  note :** 
186- The  Apache  2.0  is  a  very  permissive  license  just  like  MIT  and  BSD , but  as 
187- FSF  notes , it  includes  " certain patent termination and indemnification
188- provisions"  , which  is  a  good  thing . We  (the  authours ) cannot  come  to  you 
189- (the  users ) to  claim  any  patents  we  might  have  on  something  in  the  code .
190- 
191- If  you  have  any  compatibility  issues  with  this  license , keep  in  mind  that  if 
192- you 're using this as an external dependency (e.g. with Rebar or Erlang.mk) 
193- you' re  not  actually  distributing  this  dependency  anyway . Even  if  you  do 
194- distribute  dependencies , they  are  not  actually  linked  together  until  they 
195- are  loaded  and  run  in  the  BEAM  unless  you  compile  the  release  with  HiPE .
0 commit comments