Grpc from Scratch: Introduction and Proto Buffers
If you are reading this, you already know what gRPC is. But if you don’t here is a textbook answer. gRPC stands for Google Remote Procedure Call and runs on top of the HTTP/2 protocol, it offers some enhancements over traditional HTTP like header compression (not to be confused with gzip compression) which reduces the size of the payload and also speeds up performance.
gRPC also has another nice feature which is the Protocol Buffer; this allows developers to write a definition for their APIs and share it with other developers. It is language agnostic and supports code generation in multiple languages which makes it suitable for microservices. It also supports strong typing making it ideal for larger enterprise applications where developers are spread across multiple teams and need help communicating API specs faster. But the best feature of gRPC in my opinion is bidirectional streaming where the server and client can communicate with each other hence replacing traditional polling which means fewer resources wasted.
Proto/Protocol Buffers
A protocol buffer file or a .proto
file serves as the API definition for a gRPC application.
|
|
The syntax = "proto3";
defines the version of the proto being used. The current standard is at version 3 hence proto3
in the definition.
Next, we have the message
property. A message can be the request input or the output returned from a gRPC endpoint. It is the wrapper that contains all the properties that would be part of the input-output. It is divided into 3 parts
- datatype -gRPC supports a lot of different datatypes and here is a list of available datatypes.
- property name - the name of the field/property
- field number - this is pretty confusing but it is supposed to be like a unique labels for the different pieces of information within a
message
. They help in organizing and identifying each piece of data when you send or receive a message.
Different Types of datatypes Scalar Types:
int32, int64: Signed 32-bit and 64-bit integers.
uint32, uint64: Unsigned 32-bit and 64-bit integers.
sint32, sint64: Signed zigzag-encoded 32-bit and 64-bit integers.
fixed32, fixed64: 32-bit and 64-bit integers with fixed sizes.
sfixed32, sfixed64: Signed 32-bit and 64-bit integers with fixed sizes.
float: Single-precision floating-point numbers.
double: Double-precision floating-point numbers.
bool: Boolean values.
string: A sequence of characters, represented as a UTF-8 encoded string.
bytes: Binary data, often used for representing arbitrary binary content.
syntax = "proto3";
// Define an enum called Color
enum Color {
RED = 0;
GREEN = 1;
BLUE = 2;
YELLOW = 3;
}
// Define a message that uses the Color enum
message PaintCan {
string brand = 1;
Color color = 2;
}
// Example usage
PaintCan myCan = {
brand: "ABC Paint",
color: GREEN
};
syntax = "proto3";
message Person {
string name = 1;
repeated string email = 2;
}
Person myPerson = {
name: "Alice",
email: ["alice@example.com", "alice@gmail.com"]
};
syntax = "proto3";
// Define a message named "PhoneNumber" to represent phone numbers
message PhoneNumber {
string number = 1;
string type = 2;
}
// Define a message named "Person" with a repeated field of PhoneNumber objects
message Person {
string name = 1;
repeated PhoneNumber phone_numbers = 2;
}
// Example usage
Person myPerson = {
name: "Alice"
phone_numbers: [
{ number: "123-456-7890", type: "Home" },
{ number: "987-654-3210", type: "Work" }
]
};
Maps:
Maps are another way to define slightly complicated data; in the definition below use define a nested object/dictionary with a key which is an int32 datatype and the value which is of the Employee
datatype. NOTE: Messages also count as datatypes in gRPC
syntax = "proto3";
// Define a message named "Employee" to represent employee details, it includes a key, value pair
message Employee {
string name = 1;
string department = 2;
int32 employee_id = 3;
}
// Define a message named "Company" with a map field
message Company {
string company_name = 1;
map<int32, Employee> employees = 2;
}
// Example usage
Company myCompany = {
company_name: "ACME Corporation"
employees: {
101: {
name: "Alice",
department: "HR",
employee_id: 101
}
102: {
name: "Bob",
department: "Engineering",
employee_id: 102
}
}
};
oneof
datatypes can be used to define that a particular property will only return one of the properties mentioned in the definition.syntax = "proto3";
message Transport {
oneof vehicle {
string car_model = 1;
string bicycle_brand = 2;
string public_transport = 3;
}
}
{
"car_model": "GTR"
}
{
"bicycle_brand": "trek"
}
Timestamps: gRPC also supports a timestamp datatype but this needs to be imported from google’s proto buffer defintion using import "google/protobuf/timestamp.proto";
which brings us to the point that gRPC supports importing definitions.
Field Options: Field options can be used to define additional metadata for fields, such as whether a field is required, deprecated, or has custom serialization options.
There is still a lot more to learn about gRPC and this post only provides a starting point to the gRPC world. Hopefully I can keep adding more posts to help those who are starting out.