gRPC: A Modern Approach to RPC
I was fortunate enough to work with some coworkers who understand the importance of using varying protocols for different purposes. gRPC was a unique tool that thrives moreso in a backend service to service communication modus operandum. I've had the opportunity to provide a webservice using gRPC as a source of truth that transcodes the protobuf contract into a Restful convention, specifically using the grpc-Web libraries. Ultimately, I'm a fan of where it is right now and will remain watchful on potential future applications of this technology.
Understanding RPC Models
The essence of Remote Procedure Call lies in its model where addressable entities are procedures, and data is concealed behind these procedures. This stands in contrast to the HTTP model, where addressable entities are data resources, and behavior is abstracted behind the data.
Comparing Common APIs: REST vs. gRPC
While REST APIs require meticulous consideration of data models with concentration behind its resources, endpoints, and handling of errors, gRPC simplifies this by abstracting these complexities into a contract. Through Protocol Buffers, gRPC basically performs the entire code generation to create a more uniform experience. Its performance is noteworthy, thanks to its utilization of binary payloads and exploitation of HTTP/2 for connection management.
When to Choose gRPC
gRPC lives better in a server to server communication protocol. It likely is suitable for server-to-server communication as opposed to client side communication, which many times require middleware such as gRPC-Web, which effectively will transcode it into an HTTP interaction.
Considerations and Downsides
It requires specialized software on both client and server ends, which may pose integration challenges with proxied APIs. Additionally, its rigid structure may not always align seamlessly with certain API architectures.
Protobuf
At the core of gRPC lies Protocol Buffers, a language-agnostic data serialization format. Protocol Buffers offer several benefits, including efficient binary serialization, convenient API evolution, and seamless interoperability across various programming languages.
Example of a protobuf contract
syntax = "proto3"; // Define that we're using protobuf version 3
// Define the User service
service UserService {
// Create a new user
rpc CreateUser (CreateUserRequest) returns (CreateUserResponse);
// Get a user by ID
rpc GetUser (GetUserRequest) returns (GetUserResponse);
// Update a user
rpc UpdateUser (UpdateUserRequest) returns (UpdateUserResponse);
}
// Define the message for creating a user
message CreateUserRequest {
string name = 1;
string email = 2;
int32 age = 3;
}
// Define the response message for creating a user
message CreateUserResponse {
User user = 1;
}
// Define the message for getting a user by ID
message GetUserRequest {
int32 id = 1;
}
// Define the response message for getting a user
message GetUserResponse {
User user = 1;
Keep in mind that you can only add a new index to a message, removing or modifying an existing contract would constitute a breaking change.