gRPC Integration
Comprehensive guide to gRPC for high-performance microservices communication with Protocol Buffers.
Best use case
gRPC Integration is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Comprehensive guide to gRPC for high-performance microservices communication with Protocol Buffers.
Teams using gRPC Integration should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/grpc-integration/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How gRPC Integration Compares
| Feature / Agent | gRPC Integration | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Comprehensive guide to gRPC for high-performance microservices communication with Protocol Buffers.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# gRPC Integration
## Overview
gRPC (Google Remote Procedure Call) is a high-performance, open-source universal RPC framework that uses Protocol Buffers for serialization. It's ideal for microservices communication, providing efficient binary serialization, streaming capabilities, and built-in code generation.
## gRPC Fundamentals and Protocol Buffers
### What is gRPC?
gRPC is a modern RPC framework that:
- Uses HTTP/2 for transport
- Employs Protocol Buffers for efficient serialization
- Supports multiple programming languages
- Provides built-in streaming (unary, server streaming, client streaming, bidirectional)
- Offers code generation from service definitions
### Protocol Buffers
Protocol Buffers (protobuf) is a language-agnostic binary serialization format:
```proto
syntax = "proto3";
package user;
message User {
string id = 1;
string name = 2;
string email = 3;
repeated string roles = 4;
google.protobuf.Timestamp created_at = 5;
}
```
**Key features:**
- Binary format (smaller than JSON)
- Strong typing
- Backward/forward compatible
- Efficient serialization/deserialization
## Service Definition (.proto Files)
### Basic Service Definition
```proto
syntax = "proto3";
package user.v1;
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
service UserService {
// Unary RPC
rpc GetUser(GetUserRequest) returns (User);
// Server streaming RPC
rpc ListUsers(ListUsersRequest) returns (stream User);
// Client streaming RPC
rpc CreateUserBatch(stream CreateUserRequest) returns (CreateUserBatchResponse);
// Bidirectional streaming RPC
rpc UserEvents(stream UserEventRequest) returns (stream UserEventResponse);
}
message GetUserRequest {
string id = 1;
}
message User {
string id = 1;
string name = 2;
string email = 3;
google.protobuf.Timestamp created_at = 4;
}
message ListUsersRequest {
int32 page_size = 1;
string page_token = 2;
}
message CreateUserRequest {
string name = 1;
string email = 2;
}
message CreateUserBatchResponse {
repeated string user_ids = 1;
int32 created_count = 2;
}
message UserEventRequest {
string event_type = 1;
bytes payload = 2;
}
message UserEventResponse {
bool acknowledged = 1;
string message = 2;
}
```
### Proto File Organization
```
proto/
├── user/
│ ├── v1/
│ │ ├── user.proto
│ │ └── user_service.proto
│ └── v2/
│ └── user_service.proto
├── common/
│ ├── timestamp.proto
│ └── pagination.proto
└── google/
└── protobuf/
├── timestamp.proto
└── empty.proto
```
### Importing Common Definitions
```proto
// common/pagination.proto
syntax = "proto3";
package common;
message PageRequest {
int32 page_size = 1;
string page_token = 2;
}
message PageResponse {
string next_page_token = 1;
int32 total_size = 2;
}
// user_service.proto
syntax = "proto3";
package user.v1;
import "common/pagination.proto";
service UserService {
rpc ListUsers(common.PageRequest) returns (ListUsersResponse);
}
message ListUsersResponse {
repeated User users = 1;
common.PageResponse page_info = 2;
}
```
## Unary, Server Streaming, Client Streaming, Bidirectional Streaming
### Unary RPC
Simplest pattern - client sends request, server sends response:
```javascript
// Client
const client = new UserServiceClient('localhost:50051', credentials.createInsecure());
const request = new GetUserRequest();
request.setId('123');
client.getUser(request, (error, response) => {
if (error) {
console.error('Error:', error);
return;
}
console.log('User:', response.toObject());
});
// Server
function getUser(call, callback) {
const user = db.getUser(call.request.getId());
callback(null, user);
}
```
### Server Streaming RPC
Client sends request, server returns stream of responses:
```javascript
// Client
const call = client.listUsers(new ListUsersRequest());
call.on('data', (user) => {
console.log('User:', user.toObject());
});
call.on('end', () => {
console.log('Stream ended');
});
call.on('error', (error) => {
console.error('Error:', error);
});
// Server
function listUsers(call) {
const users = db.listUsers();
users.forEach(user => {
call.write(user);
});
call.end();
}
```
### Client Streaming RPC
Client sends stream of requests, server returns single response:
```javascript
// Client
const call = client.createUserBatch((error, response) => {
if (error) {
console.error('Error:', error);
return;
}
console.log('Created:', response.toObject());
});
const users = [{ name: 'John', email: 'john@example.com' }, ...];
users.forEach(user => {
const request = new CreateUserRequest();
request.setName(user.name);
request.setEmail(user.email);
call.write(request);
});
call.end();
// Server
function createUserBatch(call, callback) {
const userIds = [];
call.on('data', (request) => {
const userId = db.createUser(request.toObject());
userIds.push(userId);
});
call.on('end', () => {
const response = new CreateUserBatchResponse();
response.setUserIdsList(userIds);
response.setCreatedCount(userIds.length);
callback(null, response);
});
}
```
### Bidirectional Streaming RPC
Both client and server send streams:
```javascript
// Client
const call = client.userEvents();
call.on('data', (response) => {
console.log('Response:', response.toObject());
});
call.on('end', () => {
console.log('Stream ended');
});
// Send events
function sendEvent(type, payload) {
const request = new UserEventRequest();
request.setEventType(type);
request.setPayload(payload);
call.write(request);
}
// Server
function userEvents(call) {
call.on('data', (request) => {
const response = new UserEventResponse();
response.setAcknowledged(true);
response.setMessage('Event received');
call.write(response);
});
call.on('end', () => {
call.end();
});
}
```
## gRPC vs REST Comparison
| Aspect | gRPC | REST |
|--------|------|------|
| **Protocol** | HTTP/2 | HTTP/1.1 or HTTP/2 |
| **Data Format** | Protocol Buffers (binary) | JSON/XML (text) |
| **Payload Size** | Smaller (binary) | Larger (text) |
| **Code Generation** | Built-in | Manual or third-party |
| **Streaming** | Native support | Limited (SSE) |
| **Browser Support** | Requires gRPC-Web | Native |
| **Learning Curve** | Steeper | Easier |
| **Tooling** | Mature | Very mature |
| **Debugging** | Requires tools | Easy (curl, browser) |
| **Use Case** | Internal microservices | Public APIs |
**When to use gRPC:**
- Internal microservices communication
- High-performance requirements
- Need for streaming
- Strong typing and code generation
- Polyglot environments
**When to use REST:**
- Public-facing APIs
- Browser-based clients
- Simple CRUD operations
- Need for easy debugging
- Legacy system integration
## Code Generation (protoc)
### Installing protoc
```bash
# macOS
brew install protobuf
# Linux
apt-get install protobuf-compiler
# Windows
# Download from https://github.com/protocolbuffers/protobuf/releases
```
### Generating Node.js Code
```bash
# Install plugins
npm install @grpc/grpc-js @grpc/proto-loader
# Generate code
protoc --js_out=import_style=commonjs,binary:. --grpc_out=. \
--plugin=protoc-gen-grpc=$(which grpc_tools_node_protoc_plugin) \
user_service.proto
```
### Using @grpc/proto-loader (Dynamic)
```javascript
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const PROTO_PATH = './proto/user_service.proto';
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const userProto = grpc.loadPackageDefinition(packageDefinition).user.v1;
const client = new userProto.UserService(
'localhost:50051',
grpc.credentials.createInsecure()
);
```
### Generating TypeScript Code
```bash
# Install TypeScript plugin
npm install grpc_tools_node_protoc_ts ts-protoc-gen
# Generate TypeScript code
protoc \
--plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts \
--plugin=protoc-gen-grpc=./node_modules/.bin/grpc_tools_node_protoc_plugin \
--ts_out=./generated \
--grpc_out=./generated \
--proto_path=./proto \
./proto/user_service.proto
```
### Generating Go Code
```bash
# Install Go plugins
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# Generate Go code
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
user_service.proto
```
### Generating Python Code
```bash
# Install Python plugin
pip install grpcio grpcio-tools
# Generate Python code
python -m grpc_tools.protoc \
-I./proto \
--python_out=./generated \
--grpc_python_out=./generated \
./proto/user_service.proto
```
## Error Handling and Status Codes
### gRPC Status Codes
```javascript
const grpc = require('@grpc/grpc-js');
// Common status codes
grpc.status.OK // Operation completed successfully
grpc.status.CANCELLED // Operation was cancelled
grpc.status.UNKNOWN // Unknown error
grpc.status.INVALID_ARGUMENT // Client specified invalid argument
grpc.status.DEADLINE_EXCEEDED // Deadline expired before operation could complete
grpc.status.NOT_FOUND // Requested entity not found
grpc.status.ALREADY_EXISTS // Entity already exists
grpc.status.PERMISSION_DENIED // Caller lacks permission
grpc.status.UNAUTHENTICATED // Request lacks valid authentication
grpc.status.RESOURCE_EXHAUSTED // Resource exhausted (quota limit)
grpc.status.FAILED_PRECONDITION // Operation rejected because system is not in correct state
grpc.status.ABORTED // Operation was aborted
grpc.status.OUT_OF_RANGE // Operation attempted past valid range
grpc.status.UNIMPLEMENTED // Operation is not implemented
grpc.status.INTERNAL // Internal error
grpc.status.UNAVAILABLE // Service currently unavailable
grpc.status.DATA_LOSS // Unrecoverable data loss
```
### Error Handling in Server
```javascript
const grpc = require('@grpc/grpc-js');
function getUser(call, callback) {
try {
const userId = call.request.getId();
if (!userId) {
const error = new Error('User ID is required');
error.code = grpc.status.INVALID_ARGUMENT;
error.metadata = new grpc.Metadata();
error.metadata.set('field', 'id');
return callback(error);
}
const user = db.getUser(userId);
if (!user) {
const error = new Error('User not found');
error.code = grpc.status.NOT_FOUND;
return callback(error);
}
callback(null, user);
} catch (error) {
console.error('Error in getUser:', error);
const grpcError = new Error('Internal server error');
grpcError.code = grpc.status.INTERNAL;
callback(grpcError);
}
}
```
### Error Handling in Client
```javascript
client.getUser(request, (error, response) => {
if (error) {
switch (error.code) {
case grpc.status.NOT_FOUND:
console.error('User not found');
break;
case grpc.status.PERMISSION_DENIED:
console.error('Permission denied');
break;
case grpc.status.DEADLINE_EXCEEDED:
console.error('Request timeout');
break;
default:
console.error('Error:', error.message);
}
return;
}
console.log('User:', response.toObject());
});
```
### Custom Error Metadata
```javascript
// Server
function createUser(call, callback) {
try {
const { name, email } = call.request.toObject();
const validationErrors = validateUser({ name, email });
if (validationErrors.length > 0) {
const error = new Error('Validation failed');
error.code = grpc.status.INVALID_ARGUMENT;
error.metadata = new grpc.Metadata();
error.metadata.set('validation-errors', JSON.stringify(validationErrors));
return callback(error);
}
const user = db.createUser({ name, email });
callback(null, user);
} catch (error) {
callback(error);
}
}
// Client
client.createUser(request, (error, response) => {
if (error && error.code === grpc.status.INVALID_ARGUMENT) {
const validationErrors = JSON.parse(
error.metadata.get('validation-errors')[0]
);
console.error('Validation errors:', validationErrors);
}
});
```
## Interceptors and Middleware
### Server Interceptors
```javascript
const grpc = require('@grpc/grpc-js');
// Logging interceptor
const loggingInterceptor = (options, nextCall) => {
return new grpc.ServerInterceptingCall(nextCall(options), {
start: (metadata, listener, next) => {
console.log(`[Request] ${options.method_definition.path}`);
const newListener = {
onReceiveMessage: (message, next) => {
console.log(`[Message] ${JSON.stringify(message)}`);
next(message);
},
onReceiveStatus: (status, next) => {
console.log(`[Status] ${status.code} - ${status.details}`);
next(status);
},
};
next(metadata, newListener);
},
});
};
// Authentication interceptor
const authInterceptor = (options, nextCall) => {
return new grpc.ServerInterceptingCall(nextCall(options), {
start: (metadata, listener, next) => {
const token = metadata.get('authorization')[0];
if (!token) {
const error = {
code: grpc.status.UNAUTHENTICATED,
details: 'Missing authentication token',
};
return next(null, { status: error });
}
const user = verifyToken(token);
if (!user) {
const error = {
code: grpc.status.UNAUTHENTICATED,
details: 'Invalid token',
};
return next(null, { status: error });
}
// Add user to call
options.user = user;
next(metadata, listener);
},
});
};
// Apply interceptors
const server = new grpc.Server();
server.addService(userProto.UserService.service, { getUser, ... });
server.bindAsync(
'0.0.0.0:50051',
grpc.ServerCredentials.createInsecure(),
() => {
console.log('Server started');
}
);
```
### Client Interceptors
```javascript
// Retry interceptor
const retryInterceptor = (options, nextCall) => {
let retryCount = 0;
const maxRetries = 3;
const newCall = nextCall(options);
const originalStart = newCall.startWithContext;
newCall.startWithContext = function (metadata, listener, next) {
const retryListener = {
onReceiveStatus: (status, next) => {
if (status.code === grpc.status.UNAVAILABLE && retryCount < maxRetries) {
retryCount++;
console.log(`Retrying... (${retryCount}/${maxRetries})`);
setTimeout(() => {
newCall.startWithContext(metadata, listener, next);
}, 1000 * retryCount);
return;
}
next(status);
},
};
originalStart.call(this, metadata, retryListener, next);
};
return newCall;
};
// Apply client interceptors
const client = new userProto.UserService(
'localhost:50051',
grpc.credentials.createInsecure(),
{ interceptors: [retryInterceptor] }
);
```
## Authentication (TLS, JWT, API Keys)
### TLS/SSL Configuration
```javascript
const grpc = require('@grpc/grpc-js');
const fs = require('fs');
// Server with TLS
const serverCredentials = grpc.ServerCredentials.createSsl(
fs.readFileSync('ca.crt'), // CA certificate
[
{
cert_chain: fs.readFileSync('server.crt'),
private_key: fs.readFileSync('server.key'),
},
],
false // check client certificate
);
server.bindAsync(
'0.0.0.0:50051',
serverCredentials,
() => {
console.log('Secure server started');
}
);
// Client with TLS
const clientCredentials = grpc.credentials.createSsl(
fs.readFileSync('ca.crt'),
fs.readFileSync('client.key'),
fs.readFileSync('client.crt')
);
const client = new userProto.UserService(
'localhost:50051',
clientCredentials
);
```
### JWT Authentication
```javascript
const jwt = require('jsonwebtoken');
// Server JWT verification
const jwtInterceptor = (options, nextCall) => {
return new grpc.ServerInterceptingCall(nextCall(options), {
start: (metadata, listener, next) => {
const token = metadata.get('authorization')?.[0]?.replace('Bearer ', '');
if (!token) {
return next(null, {
status: { code: grpc.status.UNAUTHENTICATED, details: 'Missing token' },
});
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
options.user = decoded;
next(metadata, listener);
} catch (error) {
return next(null, {
status: { code: grpc.status.UNAUTHENTICATED, details: 'Invalid token' },
});
}
},
});
};
// Client JWT authentication
const token = jwt.sign({ userId: '123' }, process.env.JWT_SECRET);
const metadata = new grpc.Metadata();
metadata.add('authorization', `Bearer ${token}`);
client.getUser(request, metadata, (error, response) => {
// ...
});
```
### API Key Authentication
```javascript
// Server API key verification
const apiKeyInterceptor = (options, nextCall) => {
return new grpc.ServerInterceptingCall(nextCall(options), {
start: (metadata, listener, next) => {
const apiKey = metadata.get('x-api-key')?.[0];
if (!apiKey || !isValidApiKey(apiKey)) {
return next(null, {
status: { code: grpc.status.UNAUTHENTICATED, details: 'Invalid API key' },
});
}
options.apiKey = apiKey;
next(metadata, listener);
},
});
};
function isValidApiKey(apiKey) {
// Check against database or config
return process.env.API_KEYS.includes(apiKey);
}
// Client API key authentication
const metadata = new grpc.Metadata();
metadata.add('x-api-key', process.env.API_KEY);
client.getUser(request, metadata, (error, response) => {
// ...
});
```
## Load Balancing and Service Discovery
### Client-Side Load Balancing
```javascript
const { RoundRobin } = require('@grpc/grpc-js/build/src/load-balancer');
const client = new userProto.UserService(
'dns:///user-service:50051', // DNS-based service discovery
grpc.credentials.createInsecure(),
{
'grpc.load_balancing_config': [
{ round_robin: {} },
],
}
);
```
### gRPC Name Resolution
```javascript
// Custom DNS resolver
const dns = require('dns');
function resolveService(serviceName, callback) {
dns.resolveSrv(serviceName, (err, addresses) => {
if (err) {
return callback(err);
}
const targets = addresses.map(addr => ({
host: addr.name,
port: addr.port,
}));
callback(null, targets);
});
}
// Use custom resolver
const client = new userProto.UserService(
'custom:///user-service',
grpc.credentials.createInsecure()
);
```
### Service Discovery with Consul
```javascript
const Consul = require('consul');
const consul = new Consul();
async function getServiceAddress(serviceName) {
const services = await consul.catalog.service.nodes(serviceName);
if (services.length === 0) {
throw new Error(`Service ${serviceName} not found`);
}
const service = services[Math.floor(Math.random() * services.length)];
return `${service.ServiceAddress}:${service.ServicePort}`;
}
async function createClient(serviceName) {
const address = await getServiceAddress(serviceName);
return new userProto.UserService(
address,
grpc.credentials.createInsecure()
);
}
```
## gRPC-Web for Browser Clients
### Envoy Proxy Setup
```yaml
# envoy.yaml
static_resources:
listeners:
- name: grpc_web_listener
address:
socket_address:
address: 0.0.0.0
port_value: 8080
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
codec_type: AUTO
stat_prefix: grpc_web
route_config:
name: local_route
virtual_hosts:
- name: grpc_web_backend
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: grpc_backend
http_filters:
- name: envoy.filters.http.grpc_web
- name: envoy.filters.http.router
clusters:
- name: grpc_backend
connect_timeout: 5s
type: LOGICAL_DNS
http2_protocol_options: {}
load_assignment:
cluster_name: grpc_backend
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: localhost
port_value: 50051
```
### Browser Client
```javascript
import { grpc } from '@improbable-eng/grpc-web';
import { UserServiceClient } from './user_service_pb_service';
import { GetUserRequest } from './user_service_pb';
const client = new UserServiceClient('http://localhost:8080', null, null);
const request = new GetUserRequest();
request.setId('123');
client.getUser(request, {}, (err, response) => {
if (err) {
console.error('Error:', err);
return;
}
console.log('User:', response.toObject());
});
```
## Health Checking
### Standard Health Check Protocol
```proto
syntax = "proto3";
package grpc.health.v1;
service Health {
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
}
message HealthCheckRequest {
string service = 1;
}
message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
SERVICE_UNKNOWN = 3;
}
ServingStatus status = 1;
}
```
### Implementing Health Check
```javascript
const grpc = require('@grpc/grpc-js');
const healthProto = grpc.loadPackageDefinition(
protoLoader.loadSync('./proto/grpc/health/v1/health.proto')
).grpc.health.v1;
const healthStatus = {
'': healthProto.HealthCheckResponse.ServingStatus.SERVING,
'user-service': healthProto.HealthCheckResponse.ServingStatus.SERVING,
};
const healthImpl = {
check: (call, callback) => {
const service = call.request.getService();
const status = healthStatus[service] || healthProto.HealthCheckResponse.ServingStatus.SERVICE_UNKNOWN;
const response = new healthProto.HealthCheckResponse();
response.setStatus(status);
callback(null, response);
},
watch: (call) => {
const service = call.request.getService();
const interval = setInterval(() => {
const status = healthStatus[service] || healthProto.HealthCheckResponse.ServingStatus.SERVICE_UNKNOWN;
const response = new healthProto.HealthCheckResponse();
response.setStatus(status);
call.write(response);
}, 1000);
call.on('cancelled', () => {
clearInterval(interval);
call.end();
});
},
};
server.addService(healthProto.Health.service, healthImpl);
```
## Deadlines and Timeouts
### Setting Deadlines
```javascript
// Client deadline
const deadline = new Date();
deadline.setSeconds(deadline.getSeconds() + 5); // 5 second deadline
client.getUser(request, { deadline }, (error, response) => {
if (error && error.code === grpc.status.DEADLINE_EXCEEDED) {
console.error('Request timed out');
}
});
// Server handling deadline
function getUser(call, callback) {
if (call.getDeadline().getTime() < Date.now()) {
const error = new Error('Deadline exceeded');
error.code = grpc.status.DEADLINE_EXCEEDED;
return callback(error);
}
// Process request...
}
```
### Timeout Configuration
```javascript
const client = new userProto.UserService(
'localhost:50051',
grpc.credentials.createInsecure(),
{
'grpc.max_receive_message_length': -1, // Unlimited message size
'grpc.max_send_message_length': -1,
'grpc.initial_reconnect_backoff_ms': 100,
'grpc.max_reconnect_backoff_ms': 10000,
'grpc.keepalive_time_ms': 60000,
'grpc.keepalive_timeout_ms': 5000,
}
);
```
## Node.js Implementation
### Complete Server Example
```javascript
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
const { v4: uuidv4 } = require('uuid');
// Load proto file
const packageDefinition = protoLoader.loadSync('./proto/user_service.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const userProto = grpc.loadPackageDefinition(packageDefinition).user.v1;
// In-memory database
const users = new Map();
// Service implementation
const userService = {
getUser: (call, callback) => {
const user = users.get(call.request.getId());
if (!user) {
const error = new Error('User not found');
error.code = grpc.status.NOT_FOUND;
return callback(error);
}
callback(null, user);
},
createUser: (call, callback) => {
const user = {
id: uuidv4(),
name: call.request.getName(),
email: call.request.getEmail(),
createdAt: new Date(),
};
users.set(user.id, user);
callback(null, user);
},
listUsers: (call) => {
users.forEach(user => {
call.write(user);
});
call.end();
},
};
// Create server
const server = new grpc.Server();
server.addService(userProto.UserService.service, userService);
server.bindAsync(
'0.0.0.0:50051',
grpc.ServerCredentials.createInsecure(),
(error, port) => {
if (error) {
console.error('Failed to start server:', error);
return;
}
console.log(`Server running on port ${port}`);
}
);
```
### Complete Client Example
```javascript
const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');
// Load proto file
const packageDefinition = protoLoader.loadSync('./proto/user_service.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const userProto = grpc.loadPackageDefinition(packageDefinition).user.v1;
// Create client
const client = new userProto.UserService(
'localhost:50051',
grpc.credentials.createInsecure()
);
// Unary call
function getUser(id) {
return new Promise((resolve, reject) => {
const request = new userProto.GetUserRequest();
request.setId(id);
client.getUser(request, (error, response) => {
if (error) {
return reject(error);
}
resolve(response.toObject());
});
});
}
// Create user
function createUser(name, email) {
return new Promise((resolve, reject) => {
const request = new userProto.CreateUserRequest();
request.setName(name);
request.setEmail(email);
client.createUser(request, (error, response) => {
if (error) {
return reject(error);
}
resolve(response.toObject());
});
});
}
// List users (server streaming)
function listUsers() {
return new Promise((resolve, reject) => {
const request = new userProto.ListUsersRequest();
const users = [];
const call = client.listUsers(request);
call.on('data', (user) => {
users.push(user.toObject());
});
call.on('end', () => {
resolve(users);
});
call.on('error', reject);
});
}
// Usage
(async () => {
try {
// Create user
const user = await createUser('John Doe', 'john@example.com');
console.log('Created user:', user);
// Get user
const found = await getUser(user.id);
console.log('Found user:', found);
// List users
const allUsers = await listUsers();
console.log('All users:', allUsers);
} catch (error) {
console.error('Error:', error);
}
})();
```
## Python Implementation
### Server Example
```python
import grpc
from concurrent import futures
import user_service_pb2
import user_service_pb2_grpc
import uuid
from datetime import datetime
class UserService(user_service_pb2_grpc.UserServiceServicer):
def __init__(self):
self.users = {}
def GetUser(self, request, context):
user_id = request.id
if user_id not in self.users:
context.set_code(grpc.StatusCode.NOT_FOUND)
context.set_details('User not found')
return user_service_pb2.User()
user = self.users[user_id]
return user_service_pb2.User(
id=user['id'],
name=user['name'],
email=user['email'],
created_at=datetime_to_timestamp(user['created_at'])
)
def CreateUser(self, request, context):
user_id = str(uuid.uuid4())
user = {
'id': user_id,
'name': request.name,
'email': request.email,
'created_at': datetime.utcnow()
}
self.users[user_id] = user
return user_service_pb2.User(
id=user_id,
name=request.name,
email=request.email,
created_at=datetime_to_timestamp(user['created_at'])
)
def ListUsers(self, request, context):
for user in self.users.values():
yield user_service_pb2.User(
id=user['id'],
name=user['name'],
email=user['email'],
created_at=datetime_to_timestamp(user['created_at'])
)
def datetime_to_timestamp(dt):
from google.protobuf.timestamp_pb2 import Timestamp
timestamp = Timestamp()
timestamp.FromDatetime(dt)
return timestamp
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
user_service_pb2_grpc.add_UserServiceServicer_to_server(
UserService(), server
)
server.add_insecure_port('[::]:50051')
server.start()
print('Server started on port 50051')
server.wait_for_termination()
if __name__ == '__main__':
serve()
```
### Client Example
```python
import grpc
import user_service_pb2
import user_service_pb2_grpc
def get_user(stub, user_id):
request = user_service_pb2.GetUserRequest(id=user_id)
response = stub.GetUser(request)
return response
def create_user(stub, name, email):
request = user_service_pb2.CreateUserRequest(name=name, email=email)
response = stub.CreateUser(request)
return response
def list_users(stub):
request = user_service_pb2.ListUsersRequest()
for user in stub.ListUsers(request):
print(user)
def main():
with grpc.insecure_channel('localhost:50051') as channel:
stub = user_service_pb2_grpc.UserServiceStub(channel)
# Create user
user = create_user(stub, 'John Doe', 'john@example.com')
print(f'Created user: {user.id}')
# Get user
found = get_user(stub, user.id)
print(f'Found user: {found.name}')
# List users
print('All users:')
list_users(stub)
if __name__ == '__main__':
main()
```
## Go Implementation
### Server Example
```go
package main
import (
"context"
"log"
"net"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
pb "path/to/proto/user/v1"
)
type server struct {
pb.UnimplementedUserServiceServer
users map[string]*pb.User
}
func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
user, ok := s.users[req.Id]
if !ok {
return nil, status.Error(codes.NotFound, "User not found")
}
return user, nil
}
func (s *server) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.User, error) {
user := &pb.User{
Id: generateID(),
Name: req.Name,
Email: req.Email,
CreatedAt: timestamppb.Now(),
}
s.users[user.Id] = user
return user, nil
}
func (s *server) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {
for _, user := range s.users {
if err := stream.Send(user); err != nil {
return err
}
}
return nil
}
func generateID() string {
return "user-" + time.Now().Format("20060102150405")
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterUserServiceServer(s, &server{
users: make(map[string]*pb.User),
})
log.Println("Server started on port 50051")
if err := s.Serve(lis); err != nil {
log.Fatalf("Failed to serve: %v", err)
}
}
```
### Client Example
```go
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "path/to/proto/user/v1"
)
func main() {
conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()
client := pb.NewUserServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Create user
createResp, err := client.CreateUser(ctx, &pb.CreateUserRequest{
Name: "John Doe",
Email: "john@example.com",
})
if err != nil {
log.Fatalf("Failed to create user: %v", err)
}
log.Printf("Created user: %s", createResp.Id)
// Get user
getResp, err := client.GetUser(ctx, &pb.GetUserRequest{Id: createResp.Id})
if err != nil {
log.Fatalf("Failed to get user: %v", err)
}
log.Printf("Found user: %s", getResp.Name)
// List users
listResp, err := client.ListUsers(ctx, &pb.ListUsersRequest{})
if err != nil {
log.Fatalf("Failed to list users: %v", err)
}
for {
user, err := listResp.Recv()
if err != nil {
break
}
log.Printf("User: %s (%s)", user.Name, user.Email)
}
}
```
## Testing gRPC Services
### Unit Testing with Node.js
```javascript
const grpc = require('@grpc/grpc-js');
const { Server } = require('@grpc/grpc-js');
const assert = require('assert');
describe('UserService', () => {
let server;
let client;
before((done) => {
server = new Server();
server.addService(userProto.UserService.service, userService);
server.bindAsync(
'0.0.0.0:0', // Use port 0 for random available port
grpc.ServerCredentials.createInsecure(),
(error, port) => {
if (error) return done(error);
client = new userProto.UserService(
`localhost:${port}`,
grpc.credentials.createInsecure()
);
done();
}
);
});
after((done) => {
client.close();
server.tryShutdown(done);
});
it('should create and get user', (done) => {
const createRequest = new userProto.CreateUserRequest();
createRequest.setName('Test User');
createRequest.setEmail('test@example.com');
client.createUser(createRequest, (error, user) => {
assert.ifError(error);
assert.strictEqual(user.getName(), 'Test User');
const getRequest = new userProto.GetUserRequest();
getRequest.setId(user.getId());
client.getUser(getRequest, (error, foundUser) => {
assert.ifError(error);
assert.strictEqual(foundUser.getId(), user.getId());
done();
});
});
});
});
```
## Best Practices
1. **Schema Design**
- Use semantic versioning for proto packages
- Keep proto files in a separate repository for sharing
- Use well-defined common types
- Document services and messages with comments
2. **Error Handling**
- Use appropriate gRPC status codes
- Include helpful error messages
- Use metadata for additional error context
- Log errors server-side
3. **Performance**
- Use connection pooling
- Implement proper timeout/deadline handling
- Enable compression for large payloads
- Use streaming for large datasets
4. **Security**
- Always use TLS in production
- Implement proper authentication
- Use mutual TLS for service-to-service communication
- Validate all inputs
5. **Observability**
- Implement structured logging
- Use metrics for monitoring
- Enable tracing with OpenTelemetry
- Set up health checks
## Production Checklist
- [ ] Define and enforce deadlines/timeouts for every RPC (client and server).
- [ ] Standardize status codes + error details, and avoid leaking internals in messages.
- [ ] Use TLS everywhere; prefer mTLS for service-to-service traffic.
- [ ] Add observability: metrics, structured logs, and distributed tracing with correlation IDs.
- [ ] Add health checks and graceful shutdown; verify under load and deploy conditions.
## Related Skills
- `09-microservices/service-design`
- `09-microservices/service-discovery`Related Skills
moai-context7-lang-integration
Enterprise-grade Context7 MCP integration patterns for language-specific documentation access with real-time library resolution and intelligent caching
MCP Integration
Model Context Protocol (MCP) integration specialist. Use when creating MCP server configurations, implementing MCP integrations, or optimizing MCP performance. Specializes in MCP server architecture and integration patterns.
mapbox-integration-patterns
Official integration patterns for Mapbox GL JS across popular web frameworks. Covers setup, lifecycle management, token handling, search integration, and common pitfalls. Based on Mapbox's create-web-app scaffolding tool.
lsp-integration
This skill should be used when the user asks to "add LSP server", "configure language server", "set up LSP in plugin", "add code intelligence", "integrate language server protocol", "use pyright-lsp", "use typescript-lsp", "use rust-lsp", "socket transport", "initializationOptions", mentions LSP servers, or discusses extensionToLanguage mappings. Provides guidance for integrating Language Server Protocol servers into Claude Code plugins for enhanced code intelligence.
kuroco-frontend-integration
Kurocoとフロントエンドフレームワークの統合パターンおよびAI自動デプロイメント。使用キーワード:「Kuroco Nuxt」「Kuroco Next.js」「フロントエンド連携」「Nuxt3」「Nuxt2」「App Router」「Pages Router」「SSG」「SSR」「静的生成」「コンテンツ表示」「ログイン実装」「会員登録」「signup」「KurocoPages」「フロントエンド環境構築」「Vue」「React」「useAsyncData」「$fetch」「asyncData」「composable」「useAuth」「認証状態」「プロフィール取得」「profile」「generateStaticParams」「動的ルート」「v-html」「dangerouslySetInnerHTML」「XSS対策」「サードパーティCookie」「credentials include」「AI自動デプロイ」「Kuroco自動化」「サイト登録API」「自動ビルド」「自動デプロイ」「temp-upload」「presigned URL」「S3アップロード」「artifact_url」「KurocoFront」「プレビューデプロイ」「stage_url」「add_site」「site_key」「kuroco_front/deploy」「CI/CD」「フロントエンドビルド」「ZIP配備」「自動公開」「nuxt generate」「next build」「vite build」「デプロイAPI」「一時アップロード」「署名付きURL」。Nuxt/Next.jsでのKuroco連携、認証実装、SSG/SSR設定、AI自動デプロイに関する質問で使用。
integration
Backend-Frontend integration patterns expert. Type-safe API contracts with Pydantic-Zod validation sync (Python FastAPI) or Prisma-TypeScript native (Next.js). Shadcn forms connected to backend, error handling, loading states. Use when creating full-stack features.
hubspot-integration
Expert patterns for HubSpot CRM integration including OAuth authentication, CRM objects, associations, batch operations, webhooks, and custom objects. Covers Node.js and Python SDKs. Use when: hubs...
External KI Integration
Skill for accessing external AI services (ChatGPT, Claude, Hugging Face, etc.) via browser automation (Chrome Relay) and APIs to assist with tasks.
dapr-integration
Integrate Dapr building blocks for event-driven microservices - Pub/Sub, State Management, Secrets, Service Invocation, and Jobs API. Use when implementing event-driven architecture for Phase 5. (project)
benchling-integration
Benchling R&D platform integration. Access registry (DNA, proteins), inventory, ELN entries, workflows via API, build Benchling Apps, query Data Warehouse, for lab data management automation.
arch-cross-service-integration
Use when designing or implementing cross-service communication, data synchronization, or service boundary patterns.
api-integration
API 集成和设计最佳实践。用于设计、实现和优化 RESTful API、GraphQL API 或其他 API 集成。包括错误处理、认证、限流、版本控制等。