grpc

SKILL.md

gRPC

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:

  1. Create proto/user_service.proto with UserService defining GetUser, CreateUser, ListUsers (unary RPCs) and WatchUser (server streaming)
  2. Load the proto dynamically using @grpc/proto-loader
  3. Implement handlers for each RPC method, using proper status codes (NOT_FOUND, ALREADY_EXISTS)
  4. 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:

  1. Add a BatchCreateUsers(stream CreateUserRequest) returns (BatchCreateUsersResponse) RPC to the proto file
  2. Implement the server handler with call.on('data') to process each streamed request, tracking created count and failed emails
  3. Build a client script that reads the CSV, streams each row as a CreateUserRequest, and calls batch.end() when done
  4. Return the BatchCreateUsersResponse with created_count and failed_emails array

Guidelines

  1. Proto design is your API contract — review it as carefully as database schemas
  2. Never change field numbers — add new fields, deprecate old ones with reserved
  3. Use FieldMask for updates — distinguishes "not sent" from "set to empty"
  4. Always set deadlines — unbound calls leak resources; 5-30s for most RPCs
  5. Enable reflection in dev — makes debugging with grpcurl/grpcui easy
  6. Health checks on every service — standard gRPC health protocol for load balancers
  7. Interceptors for cross-cutting concerns — auth, logging, metrics, tracing
  8. Use streaming sparingly — unary RPCs are simpler to debug and load-balance
  9. Version via package nameuserservice.v1, userservice.v2
  10. Keep messages small — gRPC default max is 4MB; large payloads should be chunked
Weekly Installs
1
GitHub Stars
17
First Seen
5 days ago
Installed on
amp1
cline1
augment1
opencode1
cursor1
kimi-cli1