gRPC
Proto Definition
// proto/product.proto
syntax = "proto3";
package product;
service ProductService {
rpc GetProduct(GetProductRequest) returns (Product);
rpc ListProducts(ListProductsRequest) returns (stream Product); // Server streaming
rpc UploadProducts(stream Product) returns (UploadResponse); // Client streaming
rpc SyncProducts(stream SyncRequest) returns (stream SyncResponse); // Bidirectional
}
message Product {
string id = 1;
string name = 2;
double price = 3;
repeated string tags = 4;
google.protobuf.Timestamp created_at = 5;
}
message GetProductRequest { string id = 1; }
message ListProductsRequest {
int32 page_size = 1;
string page_token = 2;
}
message UploadResponse { int32 count = 1; }
Node.js (@grpc/grpc-js)
import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
const packageDef = protoLoader.loadSync('proto/product.proto');
const proto = grpc.loadPackageDefinition(packageDef).product as any;
// Server
const server = new grpc.Server();
server.addService(proto.ProductService.service, {
getProduct: (call: any, callback: any) => {
const product = db.findProduct(call.request.id);
callback(null, product);
},
listProducts: (call: any) => {
const products = db.listProducts(call.request);
for (const p of products) call.write(p);
call.end();
},
});
server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {
console.log('gRPC server running on port 50051');
});
// Client
const client = new proto.ProductService('localhost:50051', grpc.credentials.createInsecure());
client.getProduct({ id: '123' }, (err: any, product: any) => {
console.log(product);
});
Go (google.golang.org/grpc)
// Server implementation
type productServer struct {
pb.UnimplementedProductServiceServer
}
func (s *productServer) GetProduct(ctx context.Context, req *pb.GetProductRequest) (*pb.Product, error) {
product, err := s.db.FindProduct(req.Id)
if err != nil {
return nil, status.Errorf(codes.NotFound, "product not found: %s", req.Id)
}
return product, nil
}
func main() {
lis, _ := net.Listen("tcp", ":50051")
s := grpc.NewServer(
grpc.UnaryInterceptor(loggingInterceptor),
)
pb.RegisterProductServiceServer(s, &productServer{})
s.Serve(lis)
}
Error Handling
// Use standard gRPC status codes
import { status } from '@grpc/grpc-js';
callback({
code: status.NOT_FOUND,
message: `Product ${id} not found`,
});
callback({
code: status.INVALID_ARGUMENT,
message: 'Product name is required',
});
| Code |
Use When |
NOT_FOUND |
Resource doesn't exist |
INVALID_ARGUMENT |
Bad request data |
PERMISSION_DENIED |
Insufficient permissions |
UNAUTHENTICATED |
Missing/invalid credentials |
UNAVAILABLE |
Service temporarily down (retryable) |
Anti-Patterns
| Anti-Pattern |
Fix |
| Returning HTTP status codes |
Use gRPC status codes |
| No deadline/timeout on calls |
Always set deadline on client calls |
| Large messages (>4MB default) |
Stream large data, or increase maxReceiveMessageLength |
| No interceptors for auth/logging |
Use unary/stream interceptors |
| Proto files not versioned |
Keep .proto in source control, use package versioning |
Production Checklist
1---2name: grpc3description: gRPC service development. Protocol Buffers (protobuf), service definitions, streaming (unary, server, client, bidirectional), interceptors, and code generation for Node.js, Go, Java, and Python. USE WHEN: user mentions "gRPC", "protobuf", "Protocol Buffers", ".proto", "grpc-js", "tonic", "grpc-java", "service mesh RPC" DO NOT USE FOR: REST APIs - use `rest-api`; GraphQL - use `graphql`; WebSocket - use real-time skills4---5# gRPC67## Proto Definition89```protobuf10// proto/product.proto11syntax = "proto3";12package product;1314service ProductService {15 rpc GetProduct(GetProductRequest) returns (Product);16 rpc ListProducts(ListProductsRequest) returns (stream Product); // Server streaming17 rpc UploadProducts(stream Product) returns (UploadResponse); // Client streaming18 rpc SyncProducts(stream SyncRequest) returns (stream SyncResponse); // Bidirectional19}2021message Product {22 string id = 1;23 string name = 2;24 double price = 3;25 repeated string tags = 4;26 google.protobuf.Timestamp created_at = 5;27}2829message GetProductRequest { string id = 1; }30message ListProductsRequest {31 int32 page_size = 1;32 string page_token = 2;33}34message UploadResponse { int32 count = 1; }35```3637## Node.js (@grpc/grpc-js)3839```typescript40import * as grpc from '@grpc/grpc-js';41import * as protoLoader from '@grpc/proto-loader';4243const packageDef = protoLoader.loadSync('proto/product.proto');44const proto = grpc.loadPackageDefinition(packageDef).product as any;4546// Server47const server = new grpc.Server();48server.addService(proto.ProductService.service, {49 getProduct: (call: any, callback: any) => {50 const product = db.findProduct(call.request.id);51 callback(null, product);52 },53 listProducts: (call: any) => {54 const products = db.listProducts(call.request);55 for (const p of products) call.write(p);56 call.end();57 },58});5960server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => {61 console.log('gRPC server running on port 50051');62});6364// Client65const client = new proto.ProductService('localhost:50051', grpc.credentials.createInsecure());66client.getProduct({ id: '123' }, (err: any, product: any) => {67 console.log(product);68});69```7071## Go (google.golang.org/grpc)7273```go74// Server implementation75type productServer struct {76 pb.UnimplementedProductServiceServer77}7879func (s *productServer) GetProduct(ctx context.Context, req *pb.GetProductRequest) (*pb.Product, error) {80 product, err := s.db.FindProduct(req.Id)81 if err != nil {82 return nil, status.Errorf(codes.NotFound, "product not found: %s", req.Id)83 }84 return product, nil85}8687func main() {88 lis, _ := net.Listen("tcp", ":50051")89 s := grpc.NewServer(90 grpc.UnaryInterceptor(loggingInterceptor),91 )92 pb.RegisterProductServiceServer(s, &productServer{})93 s.Serve(lis)94}95```9697## Error Handling9899```typescript100// Use standard gRPC status codes101import { status } from '@grpc/grpc-js';102103callback({104 code: status.NOT_FOUND,105 message: `Product ${id} not found`,106});107108callback({109 code: status.INVALID_ARGUMENT,110 message: 'Product name is required',111});112```113114| Code | Use When |115|------|----------|116| `NOT_FOUND` | Resource doesn't exist |117| `INVALID_ARGUMENT` | Bad request data |118| `PERMISSION_DENIED` | Insufficient permissions |119| `UNAUTHENTICATED` | Missing/invalid credentials |120| `UNAVAILABLE` | Service temporarily down (retryable) |121122## Anti-Patterns123124| Anti-Pattern | Fix |125|--------------|-----|126| Returning HTTP status codes | Use gRPC status codes |127| No deadline/timeout on calls | Always set `deadline` on client calls |128| Large messages (>4MB default) | Stream large data, or increase `maxReceiveMessageLength` |129| No interceptors for auth/logging | Use unary/stream interceptors |130| Proto files not versioned | Keep .proto in source control, use package versioning |131132## Production Checklist133134- [ ] TLS certificates configured135- [ ] Health check service (gRPC health checking protocol)136- [ ] Interceptors for auth, logging, metrics137- [ ] Deadlines set on all client calls138- [ ] Proto file versioning strategy139- [ ] Load balancing configured (client-side or L7)