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.
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.
Artigos relacionados
Workflow Obsidian para CLAUDE.md com Claude Code
Transforme notas de trabalho do Obsidian em notas operacionais CLAUDE.md para preservar contexto.
Claude Code Revenue CTA Routing: artigos para PDF, Gumroad e consultoria
Um fluxo com Claude Code para levar leitores ao PDF grátis, Gumroad ou consultoria conforme intenção.
Regras de handoff para equipes com Claude Code: evidências, permissões, rollback e receita
Formato prático para entregar trabalho do Claude Code com prova, permissões, rollback, PDF grátis, Gumroad e consultoria.