Claude Code for gRPC: Protocol Buffers, Streaming, and Service Mesh — Claude Skills 360 Blog
Blog / Development / Claude Code for gRPC: Protocol Buffers, Streaming, and Service Mesh
Development

Claude Code for gRPC: Protocol Buffers, Streaming, and Service Mesh

Published: July 2, 2026
Read time: 9 min read
By: Claude Skills 360

gRPC solves specific problems better than REST: strongly-typed contracts via Protocol Buffers, bidirectional streaming, and high-performance binary serialization for inter-service communication. Claude Code handles the protobuf-to-code generation workflow, generates idiomatic server implementations, and understands the patterns that make gRPC services maintainable.

This guide covers gRPC with Claude Code: protobuf design, server implementation, streaming patterns, and client generation.

Protocol Buffer Design

CLAUDE.md for gRPC Projects

## gRPC Service

- Language: TypeScript (Node.js server) + TypeScript (browser client via gRPC-web)
- Protobuf: proto3 syntax, buf CLI for linting and breaking change detection
- Code generation: buf generate (replaces protoc)
- Testing: grpc-mock for integration tests

## Protobuf conventions
- Package: {company}.{service}.v1
- Service names: PascalCase noun (OrderService, not OrderManager)
- RPC methods: PascalCase verb+noun (CreateOrder, ListOrders, StreamOrderStatus)
- Message names: Singular for request/response (CreateOrderRequest not CreateOrderRequests)
- Field names: snake_case
- Always include field numbers in sequence — never reuse deleted field numbers
- Use well-known types: google.protobuf.Timestamp (not string for dates), google.protobuf.FieldMask

## Versioning
- /v1/ in package path — when breaking changes needed, create v2
- Non-breaking changes ok in v1: adding optional fields, adding RPCs
- Breaking changes that require v2: removing fields, changing field types

Service Definition

// proto/orders/v1/orders.proto
syntax = "proto3";

package company.orders.v1;

import "google/protobuf/timestamp.proto";
import "google/protobuf/field_mask.proto";

option go_package = "github.com/mycompany/services/orders/v1;ordersv1";
option java_package = "com.mycompany.orders.v1";

service OrderService {
  // Unary RPC
  rpc CreateOrder(CreateOrderRequest) returns (Order);
  rpc GetOrder(GetOrderRequest) returns (Order);
  rpc UpdateOrder(UpdateOrderRequest) returns (Order);
  rpc CancelOrder(CancelOrderRequest) returns (CancelOrderResponse);
  
  // Server-streaming: real-time order status updates
  rpc StreamOrderStatus(StreamOrderStatusRequest) returns (stream OrderStatusUpdate);
  
  // Client-streaming: bulk order import
  rpc ImportOrders(stream ImportOrderRequest) returns (ImportOrdersResponse);
  
  // Bidirectional streaming: live order processing dashboard
  rpc MonitorOrders(stream OrderFilter) returns (stream Order);
}

message Order {
  string id = 1;
  string user_id = 2;
  repeated OrderItem items = 3;
  int64 total_cents = 4;  // Amount in cents — avoid floats for money
  OrderStatus status = 5;
  google.protobuf.Timestamp created_at = 6;
  google.protobuf.Timestamp updated_at = 7;
  ShippingAddress shipping_address = 8;
}

message OrderItem {
  string product_id = 1;
  int32 quantity = 2;
  int64 price_cents = 3;
  string product_name = 4;
}

enum OrderStatus {
  ORDER_STATUS_UNSPECIFIED = 0;  // Required: proto3 default must be 0
  ORDER_STATUS_PENDING = 1;
  ORDER_STATUS_CONFIRMED = 2;
  ORDER_STATUS_SHIPPED = 3;
  ORDER_STATUS_DELIVERED = 4;
  ORDER_STATUS_CANCELLED = 5;
}

message CreateOrderRequest {
  string user_id = 1;
  repeated OrderItem items = 2;
  ShippingAddress shipping_address = 3;
  string idempotency_key = 4;  // Prevent duplicate submission
}

message UpdateOrderRequest {
  string id = 1;
  Order order = 2;
  google.protobuf.FieldMask update_mask = 3;  // Which fields to update
}

message StreamOrderStatusRequest {
  string order_id = 1;
}

message OrderStatusUpdate {
  string order_id = 1;
  OrderStatus status = 2;
  string message = 3;  // Human-readable status description
  google.protobuf.Timestamp timestamp = 4;
}

TypeScript Server Implementation

Implement the OrderService server in TypeScript.
Include proper error handling with gRPC status codes.
// src/services/order-service.ts
import {
  ServerUnaryCall,
  sendUnaryData,
  ServerWritableStream,
  ServiceError,
  status,
} from '@grpc/grpc-js';
import {
  OrderServiceServer,
  CreateOrderRequest,
  GetOrderRequest,
  Order,
  StreamOrderStatusRequest,
  OrderStatusUpdate,
} from '../generated/orders/v1/orders_grpc_pb';
import { OrderRepository } from '../repositories/order-repository';
import { OrderStatusBus } from '../lib/order-status-bus';

export class OrderServiceImpl implements OrderServiceServer {
  [method: string]: unknown;
  
  constructor(
    private readonly orders: OrderRepository,
    private readonly statusBus: OrderStatusBus,
  ) {}
  
  async createOrder(
    call: ServerUnaryCall<CreateOrderRequest, Order>,
    callback: sendUnaryData<Order>,
  ): Promise<void> {
    try {
      const request = call.request;
      
      // Validate
      if (!request.getUserId()) {
        callback({
          code: status.INVALID_ARGUMENT,
          message: 'user_id is required',
        } as ServiceError);
        return;
      }
      
      if (request.getItemsList().length === 0) {
        callback({
          code: status.INVALID_ARGUMENT,
          message: 'Order must contain at least one item',
        } as ServiceError);
        return;
      }
      
      // Idempotency check
      const idempotencyKey = request.getIdempotencyKey();
      if (idempotencyKey) {
        const existing = await this.orders.findByIdempotencyKey(idempotencyKey);
        if (existing) {
          callback(null, existing); // Return existing order
          return;
        }
      }
      
      const order = await this.orders.create({
        userId: request.getUserId(),
        items: request.getItemsList().map(item => ({
          productId: item.getProductId(),
          quantity: item.getQuantity(),
          priceCents: item.getPriceCents(),
          productName: item.getProductName(),
        })),
        shippingAddress: request.getShippingAddress()?.toObject(),
        idempotencyKey,
      });
      
      callback(null, this.orderToProto(order));
    } catch (error) {
      console.error('CreateOrder error:', error);
      callback({
        code: status.INTERNAL,
        message: 'Internal server error',
      } as ServiceError);
    }
  }
  
  // Server-streaming RPC
  streamOrderStatus(
    call: ServerWritableStream<StreamOrderStatusRequest, OrderStatusUpdate>,
  ): void {
    const orderId = call.request.getOrderId();
    
    // Subscribe to order status updates
    const unsubscribe = this.statusBus.subscribe(orderId, (update) => {
      const statusUpdate = new OrderStatusUpdate();
      statusUpdate.setOrderId(update.orderId);
      statusUpdate.setStatus(update.status);
      statusUpdate.setMessage(update.message);
      
      // write() returns false when the client can't keep up (backpressure)
      if (!call.write(statusUpdate)) {
        call.once('drain', () => {/* Continue after buffer drains */});
      }
    });
    
    // Clean up subscription when client disconnects
    call.on('cancelled', () => unsubscribe());
    call.on('error', () => unsubscribe());
    
    // Complete stream when order reaches final state
    this.statusBus.onFinalState(orderId, () => {
      unsubscribe();
      call.end();
    });
  }
}

Interceptors

Add authentication and request logging to all gRPC calls.
// src/interceptors/auth-interceptor.ts
import {
  ServerInterceptingCall,
  InterceptingCall,
  status,
  Metadata,
} from '@grpc/grpc-js';
import { verifyJWT } from '../lib/jwt';

export function authInterceptor(
  options: object,
  nextCall: (options: object) => InterceptingCall,
) {
  return new ServerInterceptingCall(nextCall(options), {
    start: function(metadata: Metadata, listener, next) {
      const token = metadata.get('authorization')[0] as string;
      
      // Skip auth for health check
      if (options.method_definition?.path === '/grpc.health.v1.Health/Check') {
        next(metadata, listener);
        return;
      }
      
      if (!token?.startsWith('Bearer ')) {
        listener.onReceiveStatus({
          code: status.UNAUTHENTICATED,
          details: 'Missing authorization token',
        });
        return;
      }
      
      const jwt = token.slice(7);
      const user = verifyJWT(jwt);
      
      if (!user) {
        listener.onReceiveStatus({
          code: status.UNAUTHENTICATED,
          details: 'Invalid or expired token',
        });
        return;
      }
      
      // Add user to metadata for use in handlers
      metadata.set('x-user-id', user.id);
      metadata.set('x-user-roles', JSON.stringify(user.roles));
      
      next(metadata, listener);
    },
  });
}

gRPC-Web for Browser Clients

Generate a TypeScript client that works in the browser.
Our frontend needs to call the OrderService.
// Generated client usage (via buf generate with connect-web plugin)
import { createConnectTransport } from '@connectrpc/connect-web';
import { createClient } from '@connectrpc/connect';
import { OrderService } from './generated/orders/v1/orders_connect';

const transport = createConnectTransport({
  baseUrl: 'https://api.myapp.com',
});

const client = createClient(OrderService, transport);

// Unary call
const order = await client.createOrder({
  userId: 'user-123',
  items: [{ productId: 'prod-1', quantity: 2, priceCents: BigInt(1999) }],
});

// Server-streaming: real-time order updates
async function trackOrder(orderId: string) {
  for await (const update of client.streamOrderStatus({ orderId })) {
    console.log('Status update:', update.status, update.message);
    
    if (update.status === OrderStatus.ORDER_STATUS_DELIVERED) {
      break; // Stop listening after delivery
    }
  }
}

For deploying gRPC services in Kubernetes with Envoy as a gateway, see the Kubernetes guide. For REST APIs where gRPC isn’t the right fit, see the API design guide. For microservices architecture decisions — when to use gRPC vs REST vs message queues — see the microservices guide. The Claude Skills 360 bundle includes gRPC skill sets for Protobuf design and server implementation. Start with the free tier to generate gRPC service definitions.

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free