Streaming replaces polling and huge responses with incremental, flow-controlled messages.
In grpc-js, streaming calls are Node.js streams in object mode:
- Server streaming: the handler receives a writable
call; itwrite()s messages and callsend(). The client gets a readable stream: listen fordata,end,errorandstatus, or usefor await. - Client streaming: the handler reads
callas a readable stream and answers once viacallback. The client gets a writable stream plus a callback. - Bidirectional: both sides are duplex streams that read and write independently.
Things to get right:
- Respect backpressure:
call.write()returnsfalsewhen the buffer is full; wait fordrain(or usepipeline) before writing more. - Handle
cancelledon the server: if the client goes away, stop producing work. - A stream ends with a single status. Errors mid-stream terminate the whole call; there is no per-message error.
- Long-lived streams need keepalives and reconnection logic on the client, because connections do drop.
Streams are ordered within a call, but there is no ordering between calls. Prefer unary RPCs with pagination for simple bounded lists; use streaming for real-time feeds, large transfers, or chatty interactive protocols.
Example
// hello.proto adds: rpc CountDown (CountRequest) returns (stream Tick);
// message CountRequest { int32 from = 1; } message Tick { int32 n = 1; }
import * as grpc from "@grpc/grpc-js";
import * as protoLoader from "@grpc/proto-loader";
const { hello } = grpc.loadPackageDefinition(protoLoader.loadSync("hello.proto"));
const server = new grpc.Server();
server.addService(hello.v1.Greeter.service, {
CountDown: (call) => {
for (let n = call.request.from; n > 0; n--) call.write({ n });
call.end();
},
});
server.bindAsync("127.0.0.1:50053", grpc.ServerCredentials.createInsecure(), async () => {
const client = new hello.v1.Greeter("127.0.0.1:50053", grpc.credentials.createInsecure());
const ticks = [];
for await (const tick of client.CountDown({ from: 3 })) ticks.push(tick.n);
console.log(ticks); // [ 3, 2, 1 ]
server.forceShutdown();
});Remember
- grpc-js streaming calls are Node object-mode streams
- Respect backpressure: honour
write()returning false - One status ends the stream; mid-stream errors end the call
- Watch for
cancelledon the server side
Watch out for
- Ignoring client cancellation and continuing to compute and write results nobody reads.