- author| Mohamad Lawand
- translator| Zhang Weibin
- planning| Ding Xiaoyun
Essentially, an API is a protocol between a server and a client that specifies how the server provides specific data based on client requests.

When building APIs, we think of different technologies. Depending on the needs, the technology we choose to develop APIs will change accordingly. In this era, there are two main technologies for creating APIs:
- gRPC
- REST
Both technologies use HTTP as the transport mechanism. Although the same underlying transport mechanisms are used, their implementations are completely different.
Let's compare these two technologies first, and then get a deeper understanding of gRPC.
REST
REST is a set of architectural constraints, not protocols or standards. API developers can use a variety of methods to implement REST.
In order for an API to be considered RESTful, we need to follow some constraints:
Client-server architecture: All requests must use HTTP as the transport mechanism;
Stateless: APIs should be stateless, which means that the server should not store any state about client sessions on the server side. Every request from the client to the server must contain all necessary information to understand the request. The server cannot use any context stored on the server side.
Cacheable: All data flowing through the client-server must be cacheable, which means it can be stored for subsequent retrieval and use.
Unified interface: There must be an interface between the client and the server so that information can be transferred in a standard form.
Layered system: All servers involved between client-side requests and server-side responses must be organized according to their responsibilities, and organized in a way that does not affect requests or responses.
gRPC
gRPC is built on the solid foundation of the RPC (Remote Procedure Call) protocol, and it has also entered the realm of APIs. gRPC is a free, open source framework developed by Google that uses HTTP/2 for API communication, hiding the HTTP implementation from API designers.
gRPC has many characteristics, so whether in terms of microservices or web/mobile API communication, it makes it the basic module of the next generation of web applications:
Interoperability: No matter what the current HTTP version is, no matter how the infrastructure changes (e.g., upgrading from HTTP 2 to HTTP 3), the protocol must be able to adapt and change.
Layered architecture: The core aspect of the technology stack must be able to evolve and upgrade independently without damaging any applications that use it.
Payload neutrality (Agnostic): Different services may require different message types and encodings, such as Protobuf (Protocol buffer), JSON, XML, etc. gRPC supports all of these formats and is able to compress payload by utilizing pluggable compression mechanisms.
Streams: gRPC allows large data sets to be streamed from the server to the client, and vice versa.
Pluggable: gRPC supports plugging in different functions and services on demand to meet our needs, such as health checks, fault recovery, and Load Balancer. Framework implementations provide extension points that allow us to plug in these functions.
Similar to docker and kubernetes, gRPC is part of the Cloud Native Foundation (CNCF).
In short, the benefits of gRPC include:
Modern and fast
open source
Leverage HTTP/2
language-neutral
Easy to add certifications, logs
To use gRPC:
We need to use Protobuf (Protocol Buffer) to define messages and services
The gRPC code will be automatically generated, and we need to provide specific implementation.
Whether on the server or client side, the. proto file supports 12 different languages.
By default, gRPC uses Google's open source Protocol Buffers mechanism to serialize structured data:
It's language neutral
Ability to generate code for any modern programming language
Data transfer is binary and efficient
highly scalable
allowing us to send large amounts of data
Allow us to extend and evolve APIs

Case study:
With today's technology trends, a more modern approach is to build microservices. In this example, let's learn about the process of building an air ticketing system:

The above picture shows a microservice-based air ticketing system. Here, there are a few key points related to this type of architecture that we need to pay attention to:
Microservices are often built in different languages. Then we can say that reservation management services can be built based on. NET, payment processing can be based on Java, and passenger information can use Node.js.
Each service has different business functions.
Suppose we now have microservices written in different languages that communicate with each other. When these microservices want to exchange information, they need to agree on something, such as:
API for exchanging data
data format
malformed
Access speed restrictions
REST is the most popular way to build APIs. However, this decision depends on many architectural considerations related to our implementation:
Design the type of data model;
What the endpoint will look like;
How to handle errors;
How many calls can a client make;
How authorization is achieved.
Taking these factors into account, let's take a look at the differences between gRPC and REST:
gRPC
Contract-first API development approach: Contracts (services and messages) are defined in the*. proto file and are the core of gRPC. This defines the API in a language-neutral way. These files can then be used by other programming languages to generate code (such as strongly typed clients and message classes).
Content is binary: HTTP/2 and Protobuf are binary protocols, and content is designed for computers and high performance.
The design of gRPC hides the complexity of remote operations. By using the gRPC library and related code generation, we don't need to care about issues such as routing, header information, and serialization. When we need to call a method on the client side, we only need to call the corresponding method.
gRPC supports bidirectional asynchronous flows: After a gRPC call establishes a flow, both the client and the server can send asynchronous flows to each other at any time. Server flows and client flows (in which case only one of the responses or requests is a flow) are also supported.
gRPC is designed for high performance and productivity in distributed applications.
REST API
Content-first API development methods (URLs, HTTP methods, JSON): Focus on readability and formatting.
The content is text-based (HTTP 1.1 and JSON), so it is human-readable. The result is that they are great for debugging, but not performance friendly.
Put more emphasis on HTTP. We need to consider some low-level issues, which is a good thing because we have a lot of control over HTTP requests.
Facing CRUD.
The widest audience: Every computer can use HTTP/1.1 and JSON, making it easy to use.
Based on these comparisons, we can see that these two methods each have their own advantages. However, we can see that gRPC provides a powerful set of features for microservice-based scenarios.
Create a server-client application using gRPC
Before starting coding, we installed the following software on our computer:
After the software installation is complete, we need to create the project structure (in this article, we will use the dotnet command directly on the terminal/command line):
dotnet new grpc -n GrpcService
We also need to configure SSL trust:
dotnet dev-certs https --trust
Next, we open this new project in VS Code and see what content has been created. We can see that we automatically have the following content:
Protos folder
services folder
In the Protos folder, we have a greet.proto file. As we mentioned earlier,. proto can define APIs in a language-neutral manner.
From this file, we can see that it contains a Greeter service and a SayHello method. We can think of the Greeter service as a controller and the SayHello method as an action. The content of the proto file is as follows:
// 声明我们可以使用的最新模式
syntax = "proto3";
// 为该 proto 定义命名空间,通常与我们的 Grpc 服务器相同
option csharp_namespace = "GrpcService";
package greet;
// 我们可以把一个服务看做一个类
service Greeter {
// 发送问候语
rpc SayHello (HelloRequest) returns (HelloReply);
}
// 请求消息类似于 C# 中的一个模型,其中会定义属性
// 这里的数字用来对属性进行排序
message HelloRequest {
string name = 1;
}
// 响应消息包含了问候语
message HelloReply {
string message = 1;
}
The SayHello method receives a HelloRequest (which is a message) and returns a HelloReply (which is also a message).
In the GreeterService file, we can see a GreeterService class that inherits from Greeter.GreeterBase, which is automatically generated from the. proto file.
In the SayHello method, we receive a request (HelloRequest) and return a response (HelloReply). They are also automatically generated for us by the. proto file.
Automatic code generation will be based on the. proto file definition as the files we need to generate. gRPC does all the heavy lifting for us in terms of code generation, routing, and serialization. All we need to do is implement the base class and override the implementation of the method.
Next, we try to run the gRPC service:
dotnet run
As you can see from the results from the automatically generated endpoints, we cannot use gRPC like we would use a web browser as a client for REST. In this case, we need to create a gRPC client to communicate with the service. For our clients, gRPC also requires a. proto file because it is a contract-first RPC framework. Currently, our web browser knows nothing about the client (we don't have a. proto file), so it doesn't know how to process requests.
We create a custom. proto file called customers.proto. This file must be created in the Protos folder, and its contents are as follows:
syntax = "proto3";
option csharp_namespace = "GrpcService";
package customers;
service Customer {
rpc GetCustomerInfo (CustomerFindModel) returns (CustomerDataModel);
}
message CustomerFindModel {
int32 userId = 1; // bool, int32, float, double, string
}
message CustomerDataModel {
string firstName = 1;
string lastName = 2;
}
After saving the above file, we need to add it to the. csproj file:
<ItemGroup>
<Protobuf Include="Protos\\customers.proto" GrpcServices="Server" />
</ItemGroup>
Now we need to build the application:
dotnet build
The next step is to add our CustomerService class to the Services folder and update its contents as follows:
public class CustomerService : Customer.CustomerBase
{
private readonly ILogger<CustomerService> _logger;
public CustomerService(ILogger<CustomerService> logger)
{
_logger = logger;
}
public override Task<CustomerDataModel> GetCustomerInfo(CustomerFindModel request, ServerCallContext context)
{
CustomerDataModel result = new CustomerDataModel();
// 这是一个用于演示的代码
// 在实际的场景中,这些信息应该从数据库中获取
// 应用中的数据不应该被硬编码
if(request.UserId == 1) {
result.FirstName = "Mohamad";
result.LastName = "Lawand";
} else if(request.UserId == 2) {
result.FirstName = "Richard";
result.LastName = "Feynman";
} else if(request.UserId == 3) {
result.FirstName = "Bruce";
result.LastName = "Wayne";
} else {
result.FirstName = "James";
result.LastName = "Bond";
}
return Task.FromResult(result);
}
}

Now we need to update the Startup.cs class to inform our applications that our newly created service has a new endpoint. To achieve this, in the Configure method (located in app.UserEndpoints), we need to add the following code:
endpoints.MapGrpcService<CustomerService>();
Notes under MacOS:
因为 MacOS 不支持 TLS 之上的 HTTP/2,所以我们需要采用如下的方案来更新 Program.cs 文件:
webBuilder.ConfigureKestrel(options =>
{
// 设置无需 TLS 的 HTTP/2 端点
options.ListenLocalhost(5000, o => o.Protocols =
HttpProtocols.Http2);
});
The next step is to create our client application:
dotnet new console -o GrpcGreeterClient
Now we need to add the necessary packages to the client console application to make it recognize gRPC. This can be achieved in the GrpcGreeterClient class:
dotnet add package Grpc.Net.Client
dotnet add package Google.Protobuf
dotnet add package Grpc.Tools
Because we need the client to have the same contract as the server, we need to add the. proto file created in the previous step to the client application. To achieve this:
First, we need to add a folder called Protos to the client project.
We need to copy the contents of the Protos folder in the gRPC greeter service to the gRPC client project, namely
greet.proto
customers.proto
- After pasting the file, we need to update the namespace to make it the same as the client application:
option csharp_namespace = "GrpcGreeterClient"; 4. We need to update the GrpcGreeterClient.csproj file to let it know about our newly added. proto file:
<ItemGroup>
<Protobuf Include="Protos\\greet.proto" GrpcServices="Client" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\\customers.proto" GrpcServices="Client" />
</ItemGroup>
This Protobuf element is the way for automatic code generation features to understand. proto files. With the above changes, we indicate here that we want the client to use the newly added. proto file.
We need to build the client and ensure that everything is built successfully:
dotnet run
Now, we add some code to the console application to make it easier to call the server side. In the Program.cs file, we need to make the following changes:
// 我们创建一个通道,它代表了客户端到服务器的连接
// 我们在这里添加的 URL 是由服务器的 Kestrel 所提供的
var channel = GrcpChannel.ForAddress("<https://localhost:5001>");
// 这个强类型的客户端是当我们添加.proto 文件时,由代码生成功能所创建的
var client = new Greeter.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest
{
Name = "Mohamad"
});
Console.WriteLine("From Server: " + response.Message);
var customerClient = new Customer.CustomerClient(channel);
var result = await customerClient.GetCustomerInfoAsync(new CustomerFindModel()
{
UserId = 1
});
Console.WriteLine($"First Name: {result.FirstName} - Last Name: {result.LastName}");
Now, we add stream processing capabilities to our application.
Let's go back to the customers.proto file and add a flow method to the Customer service:
// 我们要返回一个消费者的列表
// 但是在 gRPC 中我们不能返回列表,而是需要返回一个流
rpc GetAllCustomers (AllCustomerModel) returns (stream CustomerDataModel);
As we can see, in the return, we added the stream keyword, which means we are adding a stream made up of "multiple" replies.
At the same time, we also need to add an empty message
// 在 gRPC 中,我们不能定义具有空参数的方法
// 所以,我们定义一个空消息
message AllCustomerModel {
}
To implement this method, we need to go to the Services folder and add the following code to the CustomerService class:
public override async Task GetAllCustomers(AllCustomerModel request, IServerStreamWriter<CustomerDataModel> responseStream, ServerCallContext context)
{
var allCustomers = new List<CustomerDataModel>();
var c1 = new CustomerDataModel();
c1.Name = "Mohamad Lawand";
c1.Email = "mohamad@mail.com";
allCustomers.Add(c1);
var c2 = new CustomerDataModel();
c2.Name = "Richard Feynman";
c2.Email = "richard@physics.com";
allCustomers.Add(c2);
var c3 = new CustomerDataModel();
c3.Name = "Bruce Wayne";
c3.Email = "bruce@gotham.com";
allCustomers.Add(c3);
var c4 = new CustomerDataModel();
c4.Name = "James Bond";
c4.Email = "007@outlook.com";
allCustomers.Add(c4);
foreach(var item in allCustomers)
{
await responseStream.WriteAsync(item);
}
}
Now we need to copy the changes to the server-side customers.proto file into the client-side customers.proto file:
service Customer {
rpc GetCustomerInfo (CustomerFindModel) returns (CustomerDataModel);
// 我们要返回一个消费者的列表
// 但是在 gRPC 中我们不能返回列表,而是需要返回一个流
rpc GetAllCustomers (AllCustomerModel) returns (stream CustomerDataModel);
}
// 在 gRPC 中,我们不能定义具有空参数的方法
// 所以,我们定义一个空消息
message AllCustomerModel {
}
Now we need to build the application again:
dotnet build
The next step we need to update the Program.cs file in GrpcClientApp to handle the new streaming method:
var customerCall = customerClient.GetAllCustomers(new AllCustomerModel());
await foreach(var customer in customerCall.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"{customer.Name} {customer.Email}");
}
Now, let's go back to GrpcGreeter and update the greet.proto file to add stream methods to it:
rpc SayHelloStream(HelloRequest) returns (stream HelloReply);
As you can see, we added the keyword stream in the return, which means we are adding a stream composed of "multiple" replies. To implement this method, we need to go to the Services folder and add the following content to GreeterService:
public override async Task SayHelloStream(HelloRequest request, IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
{
for (int i = 0; i < 10; i ++)
{
await responseStream.WriteAsync(new HelloReply
{
Message = "Hello " + request.Name + " " + i
});
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
Now we need to copy the changes to the greet.proto file from the server to the client and build it. In the greet.proto file of the client application, we add the following line of code:
rpc SayHelloStream(HelloRequest) returns (stream HelloReply);
Make sure to build the application after saving the. proto file.
dotnet build
Now we can open Program.cs and use the new method:
var call = client.SayHelloStream(new HelloRequest
{
Name = "Mohamad"
});
await foreach(var item in call.ResponseStream.ReadAllAsync())
{
Console.WriteLine("Result " + item.Message);
}
This example illustrates how we implement client-server applications of gRPC in. NET 5.
concluding
We can see the power of gRPC in building applications, but it is not easy to unleash this power because building gRPC services requires more build time and coordination between client and server. When using REST, we can start consuming endpoints directly without any construction process.
gRPC will not necessarily replace REST, as both technologies have their own specific application scenarios. Please choose the right technology for your project based on your business scenario and needs.
** Author's profile: **
Mohamad Lawand 是一位坚定的、具有前瞻性的技术架构师,拥有 13 年以上的工作经验,工作范围涉及从金融机构到政府实体等众多行业。他积极主动,适应性强,擅长跨多平台的 SaaS 和区块链技术。Mohamad 还拥有一个 Youtube 频道,他会在那里分享自己的知识。
** Original link: **