Desenvolvimento gRPC com Claude Code: Protobuf, transmissão e operação
Crie um serviço gRPC executável com Claude Code, Protobuf, prazos, transmissão, autenticação e observabilidade.
Comece pelo contrato
gRPC é uma estrutura de chamada remota: o cliente chama um método de outro serviço como se fosse uma função local, mas a comunicação passa pela rede. Protocol Buffers define o contrato: serviços, métodos, mensagens de requisição, mensagens de resposta e números de campo. Com Claude Code, esse contrato precisa vir antes do servidor e do cliente.
Use a documentação oficial como base: gRPC Introduction, Core concepts, Node basics, Deadlines, Status Codes, Authentication, OpenTelemetry Metrics e o guia Protocol Buffers proto3.
Para continuar dentro do ClaudeCodeLab, leia também desenvolvimento de API em produção, microserviços com Claude Code, versionamento de API e estratégias de teste.
Casos de uso concretos
Não escolha gRPC só porque parece rápido. Ele é útil quando contrato tipado, falhas explícitas e consistência entre linguagens reduzem risco.
| Caso | Por que gRPC ajuda | O que pedir ao Claude Code |
|---|---|---|
| Serviços internos de pedidos, estoque e cobrança | O contrato evita divergência silenciosa | Criar .proto, servidor, cliente e tabela de status |
| Exportações grandes | Transmissão do servidor evita uma resposta gigante | Implementar returns (stream Item) e cancelamento |
| Equipes com Go, Node e Python | Um esquema orienta vários clientes | Documentar geração, pastas e CI |
| Ferramentas internas e agentes de IA | Métodos explícitos reduzem chamadas ambíguas | Adicionar cliente de exemplo, metadados de autenticação, prazos e logs |
A experiência prática de Masa é que um método Search amplo demais parece simples no começo, mas dificulta evolução. Separar leitura, criação e transmissão antes de pedir código ao Claude Code deixa a revisão menor.
Exemplo gRPC em Node pronto para executar
Este exemplo usa @grpc/proto-loader, então a primeira prova local não precisa de protoc.
mkdir claude-grpc-demo
cd claude-grpc-demo
npm init -y
npm install @grpc/grpc-js @grpc/proto-loader
mkdir proto
package.json:
{
"type": "commonjs",
"scripts": {
"server": "node server.js",
"client": "node client.js"
},
"dependencies": {
"@grpc/grpc-js": "latest",
"@grpc/proto-loader": "latest"
}
}
proto/task.proto:
syntax = "proto3";
package tasks.v1;
service TaskService {
rpc CreateTask(CreateTaskRequest) returns (Task);
rpc GetTask(GetTaskRequest) returns (Task);
rpc ListTasks(ListTasksRequest) returns (stream Task);
}
message Task {
string id = 1;
string title = 2;
string status = 3;
int64 created_at_unix = 4;
}
message CreateTaskRequest {
string title = 1;
}
message GetTaskRequest {
string id = 1;
}
message ListTasksRequest {
int32 limit = 1;
}
server.js:
const path = require("node:path");
const { randomUUID } = require("node:crypto");
const grpc = require("@grpc/grpc-js");
const protoLoader = require("@grpc/proto-loader");
const PROTO_PATH = path.join(__dirname, "proto", "task.proto");
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: false,
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const taskProto = grpc.loadPackageDefinition(packageDefinition).tasks.v1;
const token = process.env.DEMO_TOKEN || "dev-token";
const tasks = new Map();
function grpcError(code, message) {
const error = new Error(message);
error.code = code;
return error;
}
function assertAuthenticated(call) {
const value = call.metadata.get("authorization")[0];
if (value !== `Bearer ${token}`) {
throw grpcError(grpc.status.UNAUTHENTICATED, "UNAUTHENTICATED");
}
}
function createTask(call, callback) {
try {
assertAuthenticated(call);
const title = String(call.request.title || "").trim();
if (!title) {
return callback(grpcError(grpc.status.INVALID_ARGUMENT, "INVALID_ARGUMENT: title"));
}
const task = {
id: randomUUID(),
title,
status: "OPEN",
createdAtUnix: String(Math.floor(Date.now() / 1000)),
};
tasks.set(task.id, task);
callback(null, task);
} catch (error) {
callback(error);
}
}
function getTask(call, callback) {
try {
assertAuthenticated(call);
const task = tasks.get(call.request.id);
if (!task) {
return callback(grpcError(grpc.status.NOT_FOUND, "NOT_FOUND: task"));
}
callback(null, task);
} catch (error) {
callback(error);
}
}
function listTasks(call) {
try {
assertAuthenticated(call);
const limit = Math.min(Math.max(Number(call.request.limit) || 10, 1), 100);
for (const task of Array.from(tasks.values()).slice(0, limit)) {
call.write(task);
}
call.end();
} catch (error) {
call.destroy(error);
}
}
const server = new grpc.Server();
server.addService(taskProto.TaskService.service, { createTask, getTask, listTasks });
server.bindAsync("127.0.0.1:50051", grpc.ServerCredentials.createInsecure(), (error, port) => {
if (error) throw error;
console.log(`TaskService listening on ${port}`);
});
client.js:
const path = require("node:path");
const grpc = require("@grpc/grpc-js");
const protoLoader = require("@grpc/proto-loader");
const PROTO_PATH = path.join(__dirname, "proto", "task.proto");
const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
keepCase: false,
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const taskProto = grpc.loadPackageDefinition(packageDefinition).tasks.v1;
const client = new taskProto.TaskService("127.0.0.1:50051", grpc.credentials.createInsecure());
const metadata = new grpc.Metadata();
metadata.set("authorization", `Bearer ${process.env.DEMO_TOKEN || "dev-token"}`);
function deadline(ms) {
return new Date(Date.now() + ms);
}
function createTask(title) {
return new Promise((resolve, reject) => {
client.createTask({ title }, metadata, { deadline: deadline(1000) }, (error, task) => {
if (error) return reject(error);
resolve(task);
});
});
}
function getTask(id) {
return new Promise((resolve, reject) => {
client.getTask({ id }, metadata, { deadline: deadline(1000) }, (error, task) => {
if (error) return reject(error);
resolve(task);
});
});
}
function listTasks(limit) {
return new Promise((resolve, reject) => {
const rows = [];
const stream = client.listTasks({ limit }, metadata, { deadline: deadline(1000) });
stream.on("data", (task) => rows.push(task));
stream.on("error", reject);
stream.on("end", () => resolve(rows));
});
}
async function main() {
const created = await createTask("Claude Code gRPC");
const fetched = await getTask(created.id);
const rows = await listTasks(10);
console.log(JSON.stringify({ created, fetched, streamed: rows.length }, null, 2));
client.close();
}
main().catch((error) => {
console.error(error.code, error.details || error.message);
client.close();
process.exitCode = 1;
});
Em um terminal:
npm run server
Em outro:
npm run client
Evolução, segurança e falhas
Em Protocol Buffers, o erro mais perigoso é reutilizar números de campo. Não mude números já publicados. Ao remover um campo, reserve o número e o nome.
message Task {
string id = 1;
string title = 2;
string status = 3;
int64 created_at_unix = 4;
optional string assignee_email = 5;
reserved 6, 7;
reserved "owner_email";
}
Prazos precisam ser explícitos. A documentação do gRPC alerta que, sem prazo, o cliente pode esperar indefinidamente. Transmissão ajuda em exportações e progresso, mas precisa lidar com cancelamento e não deve guardar tudo em memória.
O exemplo usa createInsecure() apenas localmente. Em produção, use TLS: grpc.ServerCredentials.createSsl(...) no servidor e grpc.credentials.createSsl(rootCert) no cliente. Token em metadados não protege se o canal não estiver cifrado.
Observabilidade deve mostrar método, status, duração, cancelamento, prazo estourado e backend indisponível. As métricas gRPC de OpenTelemetry são uma boa base. Não devolva tudo como UNKNOWN; separe INVALID_ARGUMENT, NOT_FOUND, UNAUTHENTICATED, DEADLINE_EXCEEDED, UNAVAILABLE e RESOURCE_EXHAUSTED. Chamadas de escrita só devem ser repetidas automaticamente com chave de idempotência.
Prompt para Claude Code
Implemente gRPC TaskService.
Condições:
- Criar primeiro proto/task.proto
- Usar Node.js, @grpc/grpc-js e @grpc/proto-loader
- Implementar CreateTask, GetTask e ListTasks com transmissão do servidor
- Adicionar deadline em todos os RPCs do cliente
- Validar authorization metadata
- Documentar que produção deve trocar createInsecure por TLS
- Executar npm run client e relatar a saída
Arquivos editáveis:
- proto/task.proto
- server.js
- client.js
- package.json
Para revisão:
Revise esta implementação gRPC com findings first.
Verifique reutilização de números de campo, evolução do esquema,
deadline, cancellation, status code, memória na transmissão,
TLS/auth e observability. Ordene por gravidade e cite arquivos.
CTA e resultado verificado
Se você está aprendendo sozinho, comece pela folha gratuita do Claude Code e adapte este exemplo em um projeto descartável. Equipes que precisam governar .proto, CI, prompts de revisão, TLS e observabilidade podem seguir para treinamento e consultoria Claude Code. Materiais reutilizáveis ficam em produtos ClaudeCodeLab.
Nesta atualização, o exemplo foi executado em um diretório temporário com Node v24.14.1 e npm 11.11.0. npm install, node server.js e node client.js funcionaram, e o cliente mostrou created, fetched e streamed: 1. Esta máquina não tinha go, protoc nem os plugins Protobuf para Go; por isso o artigo usa o caminho Node que pôde ser verificado localmente.
Artigos relacionados
NoSQL/MongoDB com Claude Code: schema, índices e aggregation
Modele MongoDB com Claude Code: padrões de acesso, validação, índices, aggregation, testes e checklist de rollout.
Criar sua primeira REST API com Claude Code: CRUD, validação e testes
Crie uma REST API Express com Claude Code: CRUD, validação, erros, testes e código executável.
Design de REST API com Claude Code: endpoints, erros, paginação e idempotência
Desenhe REST APIs com Claude Code antes do código: OpenAPI, erros, paginação, idempotência e checklist.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.