It is a gRPC mode that allows to stream data in both directions at the same time. It is useful for chats and games. It is quite similar to WebSockets. Communication between Server and Client is independent.
Example
// chat.proto
syntax = "proto3";
package chat;
message ChatMessage {
string user = 1;
string message = 2;
string timestamp = 3;
}
service Chat {
rpc chatStream(stream ChatMessage) returns (stream ChatMessage); // Bidirectional streaming
} In this example, we define a ChatMessage that contains a user, a message, and a timestamp. The chatStream method allows both the client and the server to send a stream of ChatMessage objects to each other.
// server.ts
import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
import { ChatMessage } from './proto/chat'; // Import the necessary types
// Load the .proto file
const packageDefinition = protoLoader.loadSync('chat.proto', {
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const proto = grpc.loadPackageDefinition(packageDefinition) as any;
// Create typed server implementation
const chatService = {
chatStream: (call: grpc.ServerDuplexStream<ChatMessage, ChatMessage>) => {
call.on('data', (message: ChatMessage) => {
// When a message is received from the client, print it
console.log(`Received message from ${message.user}: ${message.message}`);
// Send a response back to the client
const timestamp = new Date().toISOString();
const responseMessage: ChatMessage = {
user: 'Server',
message: `Acknowledged: ${message.message}`,
timestamp,
};
// Write the response message to the stream
call.write(responseMessage);
});
call.on('end', () => {
console.log('Client has ended the stream.');
call.end(); // End the stream once the client stops sending messages
});
call.on('error', (err) => {
console.error('Error in chatStream:', err);
});
},
};
// Create and start the gRPC server
const server = new grpc.Server();
server.addService(proto.chat.Chat.service, chatService);
server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {
console.log('gRPC server running on port 50051');
server.start();
});
// client.ts
import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
// Load the .proto file
const packageDef = protoLoader.loadSync('chat.proto', {
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const grpcObject = grpc.loadPackageDefinition(packageDef) as any;
const chatPackage = grpcObject.chat.Chat;
const client = new chatPackage('localhost:50051', grpc.credentials.createInsecure());
// Create a duplex stream for bidirectional communication
const call = client.chatStream((error, response) => {
if (error) {
console.error('Error during stream:', error);
return;
}
// Handle incoming messages from the server
console.log('Server:', response.message);
});
// Sending messages to the server
const sendMessage = (user: string, message: string) => {
const timestamp = new Date().toISOString();
const chatMessage = {
user,
message,
timestamp,
};
call.write(chatMessage); // Write to the stream (send the message)
};
// Start sending messages
sendMessage('User1', 'Hello, server!');
sendMessage('User1', 'How are you?');
// Receiving messages from the server
call.on('data', (response) => {
console.log('Received from server:', response.message);
});
call.on('end', () => {
console.log('Server ended the stream');
});
call.on('error', (err) => {
console.error('Error in the client stream:', err);
});
Client sends: "Hello, server!"
Server responds: "Acknowledged: Hello, server!"
Client sends: "How are you?"
Server responds: "Acknowledged: How are you?"