grpc
Build high-performance RPC services with gRPC and Protocol Buffers. Use when a user asks to create gRPC services, define protobuf schemas, implement streaming RPCs, build microservice communication, set up service-to-service calls, implement bidirectional streaming, add interceptors/middleware to gRPC, generate client stubs, handle gRPC errors, implement health checks, configure load balancing, or build gRPC-Web for browser clients. Covers unary, server/client/bidirectional streaming, interceptors, deadlines, metadata, reflection, and production patterns.
Usage
Getting Started
- Install the skill using the command above
- Open your AI coding agent (Claude Code, Codex, Gemini CLI, or Cursor)
- Reference the skill in your prompt
- The AI will use the skill's capabilities automatically
Example Prompts
- "Review the open pull requests and summarize what needs attention"
- "Generate a changelog from the last 20 commits on the main branch"
Documentation
Overview
Build high-performance, strongly-typed RPC services using gRPC and Protocol Buffers. gRPC uses HTTP/2 for transport, protobuf for serialization (10x smaller than JSON, 5-10x faster parsing), and generates client/server code in 12+ languages. Ideal for microservice communication, real-time streaming, and performance-critical APIs.
Instructions
Step 1: Install Tools
# Protocol Buffer Compiler
brew install protobuf # macOS
apt install -y protobuf-compiler # Ubuntu/Debian
# Node.js
npm install @grpc/grpc-js @grpc/proto-loader
# Python
pip install grpcio grpcio-tools grpcio-reflection grpcio-health-checking
# Go
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Step 2: Define Protocol Buffers
// proto/user_service.proto
syntax = "proto3";
package userservice.v1;
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto";
service UserService {
rpc GetUser(GetUserRequest) returns (User);
rpc CreateUser(CreateUserRequest) returns (User);
rpc UpdateUser(UpdateUserRequest) returns (User);
rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty);
rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
rpc WatchUser(WatchUserRequest) returns (stream UserEvent); // Server streaming
rpc BatchCreateUsers(stream CreateUserRequest) returns (BatchCreateUsersResponse); // Client streaming
rpc Chat(stream ChatMessage) returns (stream ChatMessage); // Bidirectional
}
message User {
string id = 1;
string email = 2;
string name = 3;
Role role = 5;
google.protobuf.Timestamp created_at = 6;
}
enum Role { ROLE_UNSPECIFIED = 0; ROLE_USER = 1; ROLE_ADMIN = 2; }
message GetUserRequest { string id = 1; }
message CreateUserRequest { string email = 1; string name = 2; string password = 3; }
message UpdateUserRequest { string id = 1; string name = 2; google.protobuf.FieldMask update_mask = 4; }
message DeleteUserRequest { string id = 1; }
message ListUsersRequest { int32 page_size = 1; string page_token = 2; string filter = 3; }
message ListUsersResponse { repeated User users = 1; string next_page_token = 2; int32 total_count = 3; }
message WatchUserRequest { string id = 1; }
message UserEvent {
enum EventType { EVENT_TYPE_UNSPECIFIED = 0; EVENT_TYPE_UPDATED = 1; EVENT_TYPE_DELETED = 2; }
EventType type = 1; User user = 2; google.protobuf.Timestamp timestamp = 3;
}
message BatchCreateUsersResponse { int32 created_count = 1; repeated string failed_emails = 2; }
message ChatMessage { string sender_id = 1; string text = 2; google.protobuf.Timestamp timestamp = 3; }
Protobuf rules: field numbers are forever (never reuse), use UNSPECIFIED = 0 for enums, repeated for lists, FieldMask for partial updates, Timestamp for dates, version via package name (userservice.v1).
Step 3: Generate Code
# Python
python -m grpc_tools.protoc -I proto --python_out=gen --grpc_python_out=gen --pyi_out=gen proto/user_service.proto
# Go
protoc -I proto --go_out=gen --go_opt=paths=source_relative --go-grpc_out=gen --go-grpc_opt=paths=source_relative proto/user_service.proto
Node.js can load protos dynamically (no codegen needed):
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const packageDef = protoLoader.loadSync('proto/user_service.proto', {
keepCase: true, longs: String, enums: String, defaults: true, oneofs: true,
});
const proto = grpc.loadPackageDefinition(packageDef).userservice.v1;
Step 4: Server Implementation (Node.js)
const userService = {
GetUser(call, callback) {
const user = users.get(call.request.id);
if (!user) return callback({ code: grpc.status.NOT_FOUND, message: `User ${call.request.id} not found` });
callback(null, user);
},
CreateUser(call, callback) {
const { email, name } = call.request;
const user = { id: crypto.randomUUID(), email, name, role: 'ROLE_USER', created_at: { seconds: Date.now() / 1000 } };
users.set(user.id, user);
callback(null, user);
},
WatchUser(call) { // Server streaming
const interval = setInterval(() => {
const user = users.get(call.request.id);
if (user) call.write({ type: 'EVENT_TYPE_UPDATED', user, timestamp: { seconds: Date.now() / 1000 } });
}, 5000);
call.on('cancelled', () => clearInterval(interval));
},
BatchCreateUsers(call, callback) { // Client streaming
let created = 0; const failed = [];
call.on('data', (req) => { users.set(crypto.randomUUID(), { email: req.email, name: req.name }); created++; });
call.on('end', () => callback(null, { created_count: created, failed_emails: failed }));
},
Chat(call) { // Bidirectional streaming
call.on('data', (msg) => call.write({ sender_id: 'server', text: `Received: ${msg.text}` }));
call.on('end', () => call.end());
},
};
const server = new grpc.Server();
server.addService(proto.UserService.service, userService);
server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {});
Step 5: Client Implementation
const client = new proto.UserService('localhost:50051', grpc.credentials.createInsecure());
// Unary with deadline
const deadline = new Date(); deadline.setSeconds(deadline.getSeconds() + 5);
client.GetUser({ id: '123' }, { deadline }, (err, user) => { if (!err) console.log(user); });
// Server streaming
const stream = client.WatchUser({ id: '123' });
stream.on('data', (event) => console.log('Event:', event));
// Client streaming
const batch = client.BatchCreateUsers((err, resp) => console.log(`Created: ${resp.created_count}`));
batch.write({ email: 'alice@test.com', name: 'Alice' });
batch.write({ email: 'bob@test.com', name: 'Bob' });
batch.end();
Step 6: Error Handling
gRPC Status Codes:
NOT_FOUND (5) — Resource missing (like HTTP 404)
INVALID_ARGUMENT (3) — Bad input (like HTTP 400)
UNAUTHENTICATED (16) — Not authenticated (like HTTP 401)
PERMISSION_DENIED (7) — Forbidden (like HTTP 403)
ALREADY_EXISTS (6) — Duplicate (like HTTP 409)
RESOURCE_EXHAUSTED (8) — Rate limited (like HTTP 429)
DEADLINE_EXCEEDED (4) — Timeout
UNAVAILABLE (14) — Service down (like HTTP 503)
INTERNAL (13) — Server error (like HTTP 500)
Step 7: Testing with grpcurl
brew install grpcurl
grpcurl -plaintext localhost:50051 list
grpcurl -plaintext -d '{"email": "alice@test.com", "name": "Alice"}' \
localhost:50051 userservice.v1.UserService/CreateUser
Examples
Example 1: Build a user microservice with gRPC
User prompt: "Create a gRPC user service in Node.js with CRUD operations and server streaming for live user updates."
The agent will:
- Create
proto/user_service.protowithUserServicedefiningGetUser,CreateUser,ListUsers(unary RPCs) andWatchUser(server streaming) - Load the proto dynamically using
@grpc/proto-loader - Implement handlers for each RPC method, using proper status codes (
NOT_FOUND,ALREADY_EXISTS) - Start the server on port 50051 and verify with
grpcurl -plaintext localhost:50051 list
Example 2: Add batch user import with client streaming
User prompt: "Add a batch import endpoint to the user service that accepts a stream of user records from a CSV file and returns a summary of created/failed records."
The agent will:
- Add a
BatchCreateUsers(stream CreateUserRequest) returns (BatchCreateUsersResponse)RPC to the proto file - Implement the server handler with
call.on('data')to process each streamed request, tracking created count and failed emails - Build a client script that reads the CSV, streams each row as a
CreateUserRequest, and callsbatch.end()when done - Return the
BatchCreateUsersResponsewithcreated_countandfailed_emailsarray
Guidelines
- Proto design is your API contract — review it as carefully as database schemas
- Never change field numbers — add new fields, deprecate old ones with
reserved - Use
FieldMaskfor updates — distinguishes "not sent" from "set to empty" - Always set deadlines — unbound calls leak resources; 5-30s for most RPCs
- Enable reflection in dev — makes debugging with grpcurl/grpcui easy
- Health checks on every service — standard gRPC health protocol for load balancers
- Interceptors for cross-cutting concerns — auth, logging, metrics, tracing
- Use streaming sparingly — unary RPCs are simpler to debug and load-balance
- Version via package name —
userservice.v1,userservice.v2 - Keep messages small — gRPC default max is 4MB; large payloads should be chunked
Information
- Version
- 1.0.0
- Author
- terminal-skills
- Category
- Development
- License
- Apache-2.0