
Welcome to gRPC, Please Follow Me…
An introductory guide to gRPC covering fundamentals, benefits over REST, and practical implementation examples.
Originally published on Ackee Blog on May 11, 2022
In this introductory guide to gRPC, I’ll explain the fundamentals of this high-performance, open-source universal RPC framework. I’ll cover the benefits of using gRPC over traditional REST APIs and provide guidance on setting up gRPC services, sharing insights from my experience implementing gRPC in production environments.
What is gRPC?
gRPC (gRPC Remote Procedure Calls) is a modern, open-source, high-performance RPC framework that can run in any environment. Originally developed by Google, it uses HTTP/2 for transport, Protocol Buffers as the interface description language, and provides features such as authentication, bidirectional streaming, flow control, blocking or nonblocking bindings, and cancellation and timeouts.
Why Choose gRPC Over REST?
1. Performance Benefits
HTTP/2 Foundation
- Multiplexing: Multiple requests over a single connection
- Header compression: Reduced overhead
- Binary protocol: More efficient than text-based protocols
Protocol Buffers
- Smaller payload sizes compared to JSON
- Faster serialization/deserialization
- Strong typing and validation
2. Type Safety and Code Generation
service UserService {
rpc GetUser (GetUserRequest) returns (User);
rpc CreateUser (CreateUserRequest) returns (User);
}
message User {
int32 id = 1;
string name = 2;
string email = 3;
}
This definition automatically generates strongly-typed client and server code in multiple languages.
3. Streaming Support
gRPC supports four types of service methods:
- Unary RPCs: Traditional request-response
- Server streaming: Server sends multiple responses
- Client streaming: Client sends multiple requests
- Bidirectional streaming: Both sides stream independently
For more details on gRPC concepts, check the official documentation.
Setting Up Your First gRPC Service
1. Define Your Service
Create a .proto
file:
syntax = "proto3";
package blog;
service BlogService {
rpc GetPost (GetPostRequest) returns (Post);
rpc ListPosts (ListPostsRequest) returns (ListPostsResponse);
rpc CreatePost (CreatePostRequest) returns (Post);
}
message Post {
int32 id = 1;
string title = 2;
string content = 3;
string author = 4;
int64 created_at = 5;
}
message GetPostRequest {
int32 id = 1;
}
message ListPostsRequest {
int32 page_size = 1;
string page_token = 2;
}
message ListPostsResponse {
repeated Post posts = 1;
string next_page_token = 2;
}
message CreatePostRequest {
string title = 1;
string content = 2;
string author = 3;
}
2. Generate Code
# Install protoc compiler and plugins
npm install -g @grpc/grpc-js @grpc/proto-loader
# Generate TypeScript definitions
protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts \
--ts_out=grpc_js:./src/generated \
--js_out=import_style=commonjs,binary:./src/generated \
--grpc_out=grpc_js:./src/generated \
blog.proto
3. Implement the Server
import * as grpc from '@grpc/grpc-js';
import { BlogServiceService } from './generated/blog_grpc_pb';
import { Post, GetPostRequest, CreatePostRequest } from './generated/blog_pb';
class BlogServiceImpl implements BlogServiceService {
async getPost(
call: grpc.ServerUnaryCall<GetPostRequest, Post>,
callback: grpc.sendUnaryData<Post>
) {
const request = call.request;
const postId = request.getId();
// Fetch post from database
const post = await this.fetchPostFromDatabase(postId);
callback(null, post);
}
async createPost(
call: grpc.ServerUnaryCall<CreatePostRequest, Post>,
callback: grpc.sendUnaryData<Post>
) {
const request = call.request;
// Create post in database
const newPost = await this.createPostInDatabase({
title: request.getTitle(),
content: request.getContent(),
author: request.getAuthor()
});
callback(null, newPost);
}
}
// Start the server
const server = new grpc.Server();
server.addService(BlogServiceService, new BlogServiceImpl());
const port = process.env.PORT || 50051;
server.bindAsync(`0.0.0.0:${port}`, grpc.ServerCredentials.createInsecure(), () => {
console.log(`gRPC server running on port ${port}`);
server.start();
});
4. Create a Client
import * as grpc from '@grpc/grpc-js';
import { BlogServiceClient } from './generated/blog_grpc_pb';
import { GetPostRequest, CreatePostRequest } from './generated/blog_pb';
const client = new BlogServiceClient(
'localhost:50051',
grpc.credentials.createInsecure()
);
// Get a post
const getPostRequest = new GetPostRequest();
getPostRequest.setId(1);
client.getPost(getPostRequest, (error, response) => {
if (error) {
console.error('Error:', error);
return;
}
console.log('Post:', response.toObject());
});
// Create a post
const createPostRequest = new CreatePostRequest();
createPostRequest.setTitle('My First gRPC Post');
createPostRequest.setContent('This is the content of my post');
createPostRequest.setAuthor('Jaroslav Šmolík');
client.createPost(createPostRequest, (error, response) => {
if (error) {
console.error('Error:', error);
return;
}
console.log('Created post:', response.toObject());
});
Real-World Experience: Lessons Learned
Migration Strategy
When migrating from REST to gRPC:
- Start Small: Begin with internal services
- Gradual Migration: Keep REST for external APIs initially
- Gateway Pattern: Use gRPC-Gateway for HTTP compatibility
Useful Tools
- BloomRPC: A GUI client for testing gRPC services
- grpcurl: Command-line tool for interacting with gRPC servers
- buf: Modern protocol buffer tooling
- gRPC-Gateway: Generate reverse-proxy server from gRPC services
Performance Gains
In production, we observed:
- 40% reduction in payload size
- 60% improvement in latency for high-frequency calls
- Better resource utilization due to connection multiplexing
Challenges Encountered
- Browser Support: Limited direct browser support (use gRPC-Web)
- Debugging: Less human-readable than REST (use tools like BloomRPC)
- Learning Curve: Team needed time to adapt to new patterns
For more information on gRPC status codes and error handling, see the gRPC documentation.
Conclusion
gRPC represents a significant advancement in service-to-service communication, offering performance, type safety, and feature richness that traditional REST APIs struggle to match. While there’s a learning curve and some limitations (especially for browser clients), the benefits often outweigh the costs for modern distributed systems.
The key to successful gRPC adoption is starting small, focusing on internal services first, and gradually expanding as your team becomes comfortable with the technology. The investment in learning gRPC pays dividends in improved performance, better developer experience, and more robust service communication.
Whether you’re building microservices, real-time applications, or high-performance APIs, gRPC deserves serious consideration as your communication protocol of choice. Welcome to the world of modern RPC—I hope this guide helps you get started on your gRPC journey!