xref: /aosp_15_r20/external/pigweed/pw_rpc/ts/docs.rst (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1.. _module-pw_rpc-ts:
2
3-----------------
4TypeScript client
5-----------------
6.. pigweed-module-subpage::
7   :name: pw_rpc
8
9Pigweed TypeScript client provides two ways to call RPCs. The :ref:`Device <module-pw_web-device>` API is
10easier to work with if you are using the RPC via HDLC over WebSerial.
11
12If the ``device`` abstraction is not a good fit, Pigweed provides ``pw_rpc`` module,
13which makes it possible to call Pigweed RPCs from TypeScript. The module includes
14client library to facilitate handling RPCs.
15
16Creating an RPC Client
17======================
18The RPC client is instantiated from a list of channels and a set of protos.
19
20.. code-block:: typescript
21
22   import { ProtoCollection } from 'pigweedjs/protos/collection';
23
24   const channels = [new Channel(1, savePacket), new Channel(5)];
25   const client = Client.fromProtoSet(channels, new ProtoCollection());
26
27   function savePacket(packetBytes: Uint8Array): void {
28     const packet = RpcPacket.deserializeBinary(packetBytes);
29     ...
30   }
31
32To generate a ProtoSet/ProtoCollection from your own ``.proto`` files, use
33``pw_proto_compiler`` in your ``package.json`` like this:
34
35.. code-block:: javascript
36
37   ...
38   "scripts": {
39     "build-protos": "pw_proto_compiler -p protos/rpc1.proto -p protos/rpc2.proto --out dist/protos",
40
41This will generate a `collection.js` file which can be used similar to above
42example.
43
44Finding an RPC Method
45=====================
46Once the client is instantiated with the correct proto library, the target RPC
47method is found by searching based on the full name:
48``{packageName}.{serviceName}.{methodName}``
49
50.. code-block:: typescript
51
52   const channel = client.channel()!;
53   unaryStub = channel.methodStub('pw.rpc.test1.TheTestService.SomeUnary')!
54       as UnaryMethodStub;
55
56The four possible RPC stubs are ``UnaryMethodStub``,
57``ServerStreamingMethodStub``, ``ClientStreamingMethodStub``, and
58``BidirectionalStreamingMethodStub``.  Note that ``channel.methodStub()``
59returns a general stub. Since each stub type has different invoke
60parameters, the general stub should be typecast before using.
61
62Invoke an RPC with callbacks
63============================
64
65.. code-block:: typescript
66
67   invoke(request?: Message,
68       onNext: Callback = () => {},
69       onCompleted: Callback = () => {},
70       onError: Callback = () => {},
71       maxResponses: number = DEFAULT_MAX_STREAM_RESPONSES): Call
72
73All RPC methods can be invoked with a set of callbacks that are triggered when
74either a response is received, the RPC is completed, or an error occurs. The
75example below demonstrates registering these callbacks on a Bidirectional RPC.
76Other RPC types can be invoked in a similar fashion. The request parameter may
77differ slightly between RPC types.
78
79Server streaming and bidirectional streaming methods can receive many responses
80from the server. The client limits the maximum number of responses it stores for
81a single RPC call to avoid unbounded memory usage in long-running streams. Once
82the limit is reached, the oldest responses will be replaced as new ones arrives.
83By default, the limit is set to ``DEFAULT_MAX_STREAM_RESPONSES (=16384)``, but
84this can be configured on a per-call basis.
85
86.. code-block:: typescript
87
88   bidiRpc = client.channel()?.methodStub(
89       'pw.rpc.test1.TheTestService.SomeBidi')!
90       as BidirectionalStreamingMethodStub;
91
92   // Configure callback functions
93   const onNext = (response: Message) => {
94     console.log(response);
95   }
96   const onComplete = (status: Status) => {
97     console.log('completed!');
98   }
99   const onError = (error: Error) => {
100     console.log();
101   }
102
103   bidiRpc.invoke(request, onNext, onComplete, onError);
104
105Open an RPC: ignore initial errors
106=====================================
107
108Open allows you to start and register an RPC without crashing on errors. This
109is useful for starting an RPC before the server is ready. For instance, starting
110a logging RPC while the device is booting.
111
112.. code-block:: typescript
113
114   open(request?: Message,
115       onNext: Callback = () => {},
116       onCompleted: Callback = () => {},
117       onError: Callback = () => {}): Call
118
119Blocking RPCs: promise API
120==========================
121
122Each MethodStub type provides an call() function that allows sending requests
123and awaiting responses through the promise API. The timeout field is optional.
124If no timeout is specified, the RPC will wait indefinitely.
125
126Unary RPC
127---------
128.. code-block:: typescript
129
130   unaryRpc = client.channel()?.methodStub(
131       'pw.rpc.test1.TheTestService.SomeUnary')!
132       as UnaryMethodStub;
133   const request = new unaryRpc.requestType();
134   request.setFooProperty(4);
135   const timeout = 2000 // 2 seconds
136   const [status, response] = await unaryRpc.call(request, timeout);
137
138Server Streaming RPC
139--------------------
140.. code-block:: typescript
141
142   serverStreamRpc = client.channel()?.methodStub(
143       'pw.rpc.test1.TheTestService.SomeServerStreaming')!
144       as ServerStreamingMethodStub;
145
146   const call = serverStreamRpc.invoke();
147   const timeout = 2000
148   for await (const response of call.getResponses(2, timeout)) {
149    console.log(response);
150   }
151   const responses = call.getResponses() // All responses until stream end.
152   while (!responses.done) {
153     console.log(await responses.value());
154   }
155
156
157Client Streaming RPC
158--------------------
159.. code-block:: typescript
160
161   clientStreamRpc = client.channel()!.methodStub(
162     'pw.rpc.test1.TheTestService.SomeClientStreaming')!
163     as ClientStreamingMethodStub;
164   clientStreamRpc.invoke();
165   const request = new clientStreamRpc.method.requestType();
166   request.setFooProperty('foo_test');
167   clientStreamRpc.send(request);
168
169   // Send three more requests, end the stream, and wait for a response.
170   const timeout = 2000 // 2 seconds
171   request.finishAndWait([request, request, request], timeout)
172       .then((status, response) => {
173         console.log(`Client stream finished successfully: ${response}`);
174       })
175       .catch((reason) => {
176         console.log(`Client stream error: ${reason}`);
177       });
178
179Bidirectional Stream RPC
180------------------------
181.. code-block:: typescript
182
183   bidiStreamingRpc = client.channel()!.methodStub(
184     'pw.rpc.test1.TheTestService.SomeBidiStreaming')!
185     as BidirectionalStreamingMethodStub;
186   bidiStreamingRpc.invoke();
187   const request = new bidiStreamingRpc.method.requestType();
188   request.setFooProperty('foo_test');
189
190   // Send requests
191   bidiStreamingRpc.send(request);
192
193   // Receive responses
194   const timeout = 2000 // 2 seconds
195   for await (const response of call.getResponses(1, timeout)) {
196    console.log(response);
197   }
198
199   // Send three more requests, end the stream, and wait for a response.
200   request.finishAndWait([request, request, request], timeout)
201       .then(() => {
202         console.log('Bidirectional stream finished successfully');
203       })
204       .catch((reason) => {
205         console.log(`Bidirectional stream error: ${reason}`);
206       });
207