11# gentle_rpc
22
3- JSON-RPC 2.0 library with WebSockets and HTTP support for
3+ JSON-RPC 2.0 library with HTTP and WebSockets support for
44[ deno] ( https://github.com/denoland/deno ) and the browser.
55
66## Server
77
88### respond
99
10- Takes the arguments ` methods ` , ` req ` and ` options ` . You can set options for an
11- additional server argument, protocol, headers and public error stacks.
10+ Takes ` Methods ` , ` ServerRequest ` and ` Options ` .
1211
1312``` typescript
14- import { serve } from " https://deno.land/std@0.97.0/http/server.ts" ;
15- import { respond } from " https://deno.land/x/gentle_rpc/mod.ts" ;
13+ import { serve } from " https://deno.land/std@0.97.0/http/server.ts"
14+ import { respond } from " https://deno.land/x/gentle_rpc/mod.ts"
1615
17- const server = serve (" 0.0.0.0:8000" );
16+ const server = serve (" 0.0.0.0:8000" )
1817const rpcMethods = {
19- sayHello : (w : [string ]) => ` Hello ${w } ` ,
18+ sayHello : ([ w ] : [string ]) => ` Hello ${w } ` ,
2019 callNamedParameters : ({ a , b , c }: { a: number ; b: number ; c: string }) =>
2120 ` ${c } ${a * b } ` ,
2221 animalsMakeNoise : (noise : string []) =>
2322 noise .map ((el ) => el .toUpperCase ()).join (" " ),
24- };
23+ }
2524
26- console .log (" listening on 0.0.0.0:8000" );
25+ console .log (" listening on 0.0.0.0:8000" )
2726
2827for await (const req of server ) {
2928 // HTTP:
30- await respond (rpcMethods , req );
29+ respond (rpcMethods , req )
3130 // WebSockets:
32- await respond (rpcMethods , req , { proto: " ws" });
31+ respond (rpcMethods , req , { proto: " ws" })
3332}
3433```
3534
36- #### custom errors
35+ #### CustomError
3736
3837Throw a ` CustomError ` to send a server-defined error response.
3938
4039``` typescript
41- import { CustomError , respond } from " https://deno.land/x/gentle_rpc/mod.ts" ;
40+ import { CustomError , respond } from " https://deno.land/x/gentle_rpc/mod.ts"
4241
4342// ..
44- await respond ({
45- throwError : () => {
46- throw new CustomError (
47- - 32000 , // the JSON-RPC error code. Note, must be -32000 to -32099
48- " An error occurred" , // the error message, a short sentence
49- { details: " ..." }, // optional additional details, can be an object, primitive, etc (any)
50- );
43+ await respond (
44+ {
45+ throwError : () => {
46+ throw new CustomError (
47+ - 32040 , // the JSON-RPC error code. Note, must be -32040 to -32099
48+ " An error occurred" , // the error message, a short sentence
49+ { details: " ..." } // optional additional details, can be any `JsonValue`
50+ )
51+ },
5152 },
52- }, req );
53+ req
54+ )
5355// ..
5456```
5557
5658## Client
5759
5860#### createRemote
5961
60- Takes a ` resource ` for HTTP or a ` WebSocket ` for WebSockets and returns a
61- TypeScript ` Proxy ` or ` Promise<Proxy> ` which we will call ` remote ` from now on .
62+ Takes a ` Resource ` for HTTP or a ` WebSocket ` for WebSockets and returns
63+ ` Remote ` .
6264
6365``` typescript
64- import { createRemote } from " https://deno.land/x/gentle_rpc/mod.ts" ;
66+ import { createRemote } from " https://deno.land/x/gentle_rpc/mod.ts"
6567// Or import directly into the browser with:
66- import { createRemote } from " https://cdn.jsdelivr.net/gh/timonson/gentle_rpc@v2.8/client/dist/remote.js" ;
68+ import { createRemote } from " https://cdn.jsdelivr.net/gh/timonson/gentle_rpc@v2.8/client/dist/remote.js"
6769
6870// HTTP:
69- const remote = createRemote (" http://0.0.0.0:8000" );
71+ const remote = createRemote (" http://0.0.0.0:8000" )
7072
7173// WebSocket:
72- const remote = await createRemote (new WebSocket (" ws://0.0.0.0:8000" ));
74+ const remote = await createRemote (new WebSocket (" ws://0.0.0.0:8000" ))
7375```
7476
7577### HTTP
7678
77- #### remote
79+ #### call
7880
79- All ` remote ` methods take an ` Array<JsonValue> ` or ` Record<string, JsonValue> `
80- object and return ` Promise<JsonValue | undefined > ` .
81+ Takes a string and an ` Array<JsonValue> ` or ` Record<string, JsonValue> ` object
82+ and returns ` Promise<JsonValue> ` .
8183
8284``` typescript
83- const greeting = await remote .sayHello ( [" World" ]);
85+ const greeting = await remote .call ( " sayHello " , [" World" ])
8486// Hello World
8587
86- const namedParams = await remote .callNamedParameters ( {
88+ const namedParams = await remote .call ( " callNamedParameters " , {
8789 a: 5 ,
8890 b: 10 ,
8991 c: " result:" ,
90- });
92+ })
9193// result: 50
9294```
9395
9496##### notification
9597
98+ Using the option ` { isNotification: true } ` will retun ` Promise<undefined> ` .
99+
96100``` typescript
97- const notification = await remote .sayHello .notify ([" World" ]);
101+ const notification = await remote .call (" sayHello" , [" World" ], {
102+ isNotification: true ,
103+ })
98104// undefined
99105```
100106
101- ##### auth
107+ ##### jwt
102108
103- This method will set the ` Authorization ` header to `` `Bearer ${jwt}` `` .
109+ Adding a jwt to the optional property ` jwt ` will set the ` Authorization ` header
110+ to `` `Bearer ${jwt}` `` .
104111
105112``` typescript
106- const greeting = await remote .sayHello . auth ( jwt )([ " World " ]);
107- // Hello World
113+ const user = await remote .call ( " login " , undefined , { jwt })
114+ // Bob
108115```
109116
110- ##### batch
117+ #### batch
111118
112119``` typescript
113- const noise1 = await remote .animalsMakeNoise .batch ([
114- [" miaaow" ],
115- [" wuuuufu" , " wuuuufu" ],
116- [" iaaaiaia" , " iaaaiaia" , " iaaaiaia" ],
117- [" fiiiiire" ],
118- ]);
119- // [ "MIAAOW", "WUUUUFU WUUUUFU", "IAAAIAIA IAAAIAIA IAAAIAIA", "FIIIIIRE" ]
120+ const noise1 = await remote .batch ([
121+ {
122+ animalsMakeNoise: [
123+ [" miaaow" ],
124+ [" wuuuufu" , " wuuuufu" ],
125+ [" iaaaiaia" , " iaaaiaia" , " iaaaiaia" ],
126+ [" fiiiiire" ],
127+ ],
128+ sayHello: [[" World" ], undefined , [" World" ]],
129+ },
130+ ])
131+ // [ "MIAAOW", "WUUUUFU WUUUUFU", "IAAAIAIA IAAAIAIA IAAAIAIA", "FIIIIIRE", "Hello World", "Hello ", "Hello World" ]
120132```
121133
122- ##### batch with different methods
123-
124- Takes either a ` batchObject ` or a ` batchArray ` as argument and returns a
125- promise.
134+ The following example uses the object keys ` cat ` , ` dog ` , ` donkey ` , ` dragon ` as
135+ RPC _ request object ids_ under the hood. The returned _ RPC result_ values will
136+ be assigned to these keys.
126137
127138``` typescript
128- await remote .batch ({
139+ let noise2 = await remote .batch ({
129140 cat: [" sayHello" , [" miaaow" ]],
130141 dog: [" animalsMakeNoise" , [" wuuuufu" ]],
131142 donkey: [" sayHello" ],
132143 dragon: [" animalsMakeNoise" , [" fiiiiire" , " fiiiiire" ]],
133- });
144+ })
134145// { cat: "Hello miaaow", dog: "WUUUUFU", donkey: "Hello ", dragon: "FIIIIIRE FIIIIIRE" }
135146```
136147
137- The example above uses the object keys ` cat ` , ` dog ` , ` donkey ` , ` dragon ` as RPC
138- _ request object ids_ under the hood. The returned _ RPC result_ values will be
139- assigned to these keys.
140-
141- For other use cases you might prefer the following example:
142-
143- ``` typescript
144- await remote .batch ([
145- " animalsMakeNoise" ,
146- [" miaaow" ],
147- [" wuuuufu" , " wuuuufu" ],
148- [" iaaaiaia" , " iaaaiaia" , " iaaaiaia" ],
149- [" fiiiiire" ],
150- ]);
151- // [ "MIAAOW", "WUUUUFU WUUUUFU", "IAAAIAIA IAAAIAIA IAAAIAIA", "FIIIIIRE" ]
152- ```
153-
154148### WebSockets
155149
156150The support for WebSockets is still experimental and has not been fully tested
157151yet.
158152
159- #### remote
153+ #### call
160154
161- All ` remote ` methods take an ` Array<JsonValue> ` or ` Record<string, JsonValue> `
162- object and return ` Promise<JsonValue | undefined> ` .
155+ Takes a string and an ` Array<JsonValue> ` or ` Record<string, JsonValue> ` object
156+ and returns ` Promise<JsonValue | undefined> ` .
163157
164158``` typescript
165- const noise = await remote .animalsMakeNoise ([" wuufff" ]);
166- console .log (noise );
159+ const noise = await remote .call (" callNamedParameters" , {
160+ a: 10 ,
161+ b: 20 ,
162+ c: " The result is:" ,
163+ })
164+ // The result is: 200
165+
166+ const notification = await remote .call (" sayHello" , [" World" ], true )
167+ // undefined
167168
168- remote .socket .close ();
169+ remote .socket .close ()
169170```
170171
171172##### notification
172173
173174``` typescript
174- const notification = await remote .animalsMakeNoise .notify ([" wuufff" ]);
175+ const notification = await remote .animalsMakeNoise .notify ([" wuufff" ])
175176```
176177
177178##### messaging between multiple clients
@@ -185,41 +186,61 @@ Other clients can _listen to_ and _emit_ messages by _subscribing_ to the same
185186method.
186187
187188``` typescript
188- // First client
189- export async function run(iter : AsyncGenerator <unknown >) {
189+ const firstClient = await createRemote (new WebSocket (" ws://0.0.0.0:8000" ))
190+ const secondClient = await createRemote (new WebSocket (" ws://0.0.0.0:8000" ))
191+
192+ async function run(iter : AsyncGenerator <unknown >) {
190193 try {
191194 for await (let x of iter ) {
192- console .log (x );
195+ console .log (x )
193196 }
194197 } catch (err ) {
195- console .log (err .message , err .code );
198+ console .log (err .message , err .code , err . data )
196199 }
197200}
198201
199- const greeting = remote .sayHello .subscribe ();
200- run (greeting .generator );
201- greeting .emit ([" first" ]);
202+ const greeting = firstClient .subscribe (" sayHello" )
203+ const second = secondClient .subscribe (" sayHello" )
204+
205+ run (greeting .generator )
206+ run (second .generator )
207+ greeting .emit ({ w: " first" })
208+ second .emitBatch ([{ w: " second" }, { w: " third" }])
209+ // Hello first
202210// Hello first
203211// Hello second
212+ // Hello second
213+ // Hello third
204214// Hello third
205215```
206216
217+ ## Proxy API
218+
219+ Optionally, you can import _ syntactical sugar_ and use a more friendly API
220+ supported by ` Proxy ` objects.
221+
207222``` typescript
208- // Second client
209- const greeting = remote .sayHello .subscribe ();
210- run (greeting .generator );
211- greeting .emitBatch ([[" second" ], [" third" ]]);
212- // Hello first
213- // Hello second
214- // Hello third
223+ import { createRemote , HttpProxy , httpProxyHandler } from " ../../mod.ts"
224+
225+ const remote = new Proxy <HttpProxy >(
226+ createRemote (" http://0.0.0.0:8000" ),
227+ httpProxyHandler
228+ )
215229
216- // You can optionally unsubscribe:
217- greeting .unsubscribe ();
230+ let greeting = await remote .sayHello ([" World" ])
231+ // Hello World
232+
233+ const namedParams = await remote .callNamedParameters ({
234+ a: 5 ,
235+ b: 10 ,
236+ c: " result:" ,
237+ })
238+ // result: 50
218239```
219240
220241## Examples and Tests
221242
222- Checkout the
243+ Please checkout the
223244[ examples] ( https://github.com/timonson/gentle_rpc/tree/master/examples ) and
224245[ tests] ( https://github.com/timonson/gentle_rpc/tree/master/tests ) folders for
225246more detailed examples.
0 commit comments