1.. _module-pw_rpc-ts: 2 3------------------------- 4pw_rpc Typescript package 5------------------------- 6The ``pw_rpc`` Typescript package makes it possible to call Pigweed RPCs from 7Typescript. The package includes client library to facilitate handling RPCs. 8 9This package is currently a work in progress. 10 11Creating an RPC Client 12====================== 13The RPC client is instantiated from a list of channels and a set of protos. 14 15.. code-block:: typescript 16 17 const testProtoPath = 'pw_rpc/ts/test_protos-descriptor-set.proto.bin'; 18 const lib = await Library.fromFileDescriptorSet( 19 testProtoPath, 'test_protos_tspb'); 20 const channels = [new Channel(1, savePacket), new Channel(5)]; 21 const client = Client.fromProtoSet(channels, lib); 22 23 function savePacket(packetBytes: Uint8Array): void { 24 const packet = RpcPacket.deserializeBinary(packetBytes); 25 ... 26 } 27 28The proto library must match the proto build rules. The first argument 29corresponds with the location of the ``proto_library`` build rule that generates 30a descriptor set for all src protos. The second argument corresponds with the 31name of the ``js_proto_library`` build rule that generates javascript based on 32the descriptor set. For instance, the previous example corresponds with the 33following build file: ``pw_rpc/ts/BUILD.bazel``. 34 35.. code-block:: 36 37 proto_library( 38 name = "test_protos", 39 srcs = [ 40 "test.proto", 41 "test2.proto", 42 ], 43 ) 44 45 js_proto_library( 46 name = "test_protos_tspb", 47 protos = [":test_protos"], 48 ) 49 50Finding an RPC Method 51===================== 52Once the client is instantiated with the correct proto library, the target RPC 53method is found by searching based on the full name: 54``{packageName}.{serviceName}.{methodName}`` 55 56.. code-block:: typescript 57 58 const channel = client.channel()!; 59 unaryStub = channel.methodStub('pw.rpc.test1.TheTestService.SomeUnary')! 60 as UnaryMethodStub; 61 62The four possible RPC stubs are ``UnaryMethodStub``, 63``ServerStreamingMethodStub``, ``ClientStreamingMethodStub``, and 64``BidirectionalStreamingMethodStub``. Note that ``channel.methodStub()`` 65returns a general stub. Since each stub type has different invoke 66parameters, the general stub should be typecast before using. 67 68Invoke an RPC with callbacks 69============================ 70 71.. code-block:: typescript 72 73 invoke(request?: Message, 74 onNext: Callback = () => {}, 75 onCompleted: Callback = () => {}, 76 onError: Callback = () => {}): Call 77 78All RPC methods can be invoked with a set of callbacks that are triggered when 79either a response is received, the RPC is completed, or an error occurs. The 80example below demonstrates registering these callbacks on a Bidirectional RPC. 81Other RPC types can be invoked in a similar fashion. The request parameter may 82differ slightly between RPC types. 83 84.. code-block:: typescript 85 86 bidiRpc = client.channel()?.methodStub( 87 'pw.rpc.test1.TheTestService.SomeBidi')! 88 as BidirectionalStreamingMethodStub; 89 90 // Configure callback functions 91 const onNext = (response: Message) => { 92 console.log(response); 93 } 94 const onComplete = (status: Status) => { 95 console.log('completed!'); 96 } 97 const onError = (error: Error) => { 98 console.log(); 99 } 100 101 bidiRpc.invoke(request, onNext, onComplete, onError); 102 103Open an RPC: ignore initial errors 104===================================== 105 106Open allows you to start and register an RPC without crashing on errors. This 107is useful for starting an RPC before the server is ready. For instance, starting 108a logging RPC while the device is booting. 109 110.. code-block:: typescript 111 112 open(request?: Message, 113 onNext: Callback = () => {}, 114 onCompleted: Callback = () => {}, 115 onError: Callback = () => {}): Call 116 117Blocking RPCs: promise API 118========================== 119 120Each MethodStub type provides an call() function that allows sending requests 121and awaiting responses through the promise API. The timeout field is optional. 122If no timeout is specified, the RPC will wait indefinitely. 123 124Unary RPC 125--------- 126.. code-block:: typescript 127 128 unaryRpc = client.channel()?.methodStub( 129 'pw.rpc.test1.TheTestService.SomeUnary')! 130 as UnaryMethodStub; 131 const request = new unaryRpc.requestType(); 132 request.setFooProperty(4); 133 const timeout = 2000 // 2 seconds 134 const [status, response] = await unaryRpc.call(request, timeout); 135 136Server Streaming RPC 137-------------------- 138.. code-block:: typescript 139 140 serverStreamRpc = client.channel()?.methodStub( 141 'pw.rpc.test1.TheTestService.SomeServerStreaming')! 142 as ServerStreamingMethodStub; 143 144 const call = serverStreamRpc.invoke(); 145 const timeout = 2000 146 for await (const response of call.getResponses(2, timeout)) { 147 console.log(response); 148 } 149 const responses = call.getResponse() // All responses until stream end. 150 while (!responses.done) { 151 console.log(await responses.value()); 152 } 153 154 155Client Streaming RPC 156-------------------- 157.. code-block:: typescript 158 159 clientStreamRpc = client.channel()!.methodStub( 160 'pw.rpc.test1.TheTestService.SomeClientStreaming')! 161 as ClientStreamingMethodStub; 162 clientStreamRpc.invoke(); 163 const request = new clientStreamRpc.method.requestType(); 164 request.setFooProperty('foo_test'); 165 clientStreamRpc.send(request); 166 167 // Send three more requests, end the stream, and wait for a response. 168 const timeout = 2000 // 2 seconds 169 request.finishAndWait([request, request, request], timeout) 170 .then(() => { 171 console.log('Client stream finished successfully'); 172 }) 173 .catch((reason) => { 174 console.log(`Client stream error: ${reason}`); 175 }); 176 177Bidirectional Stream RPC 178------------------------ 179.. code-block:: typescript 180 181 bidiStreamingRpc = client.channel()!.methodStub( 182 'pw.rpc.test1.TheTestService.SomeBidiStreaming')! 183 as BidirectionalStreamingMethodStub; 184 bidiStreamingRpc.invoke(); 185 const request = new bidiStreamingRpc.method.requestType(); 186 request.setFooProperty('foo_test'); 187 188 // Send requests 189 bidiStreamingRpc.send(request); 190 191 // Receive responses 192 const timeout = 2000 // 2 seconds 193 for await (const response of call.getResponses(1, timeout)) { 194 console.log(response); 195 } 196 197 // Send three more requests, end the stream, and wait for a response. 198 request.finishAndWait([request, request, request], timeout) 199 .then(() => { 200 console.log('Bidirectional stream finished successfully'); 201 }) 202 .catch((reason) => { 203 console.log(`Bidirectional stream error: ${reason}`); 204 }); 205