Desarrollo gRPC con Claude Code: Protobuf, transmisión y operación
Crea un servicio gRPC ejecutable con Claude Code, Protobuf, plazos, transmisión, autenticación y observabilidad.
Empieza por el contrato, no por el servidor
gRPC es un marco de llamadas remotas: el cliente invoca un método de otro servicio como si fuera una función local, pero la comunicación ocurre por red. Protocol Buffers define el contrato: servicios, métodos, mensajes de petición, mensajes de respuesta y números de campo. Claude Code puede escribir mucho código rápido, pero si ese contrato queda ambiguo, también quedarán ambiguos el servidor, el cliente y las pruebas.
Trabaja con documentación oficial abierta: gRPC Introduction, Core concepts, Node basics, Deadlines, Status Codes, Authentication, OpenTelemetry Metrics y la guía de Protocol Buffers proto3.
Para ampliar dentro de ClaudeCodeLab, combina esta guía con desarrollo de API de producción, microservicios con Claude Code, versionado de API y estrategias de prueba.
Casos de uso reales
No elijas gRPC solo porque “es rápido”. Elige gRPC cuando el contrato tipado, los fallos explícitos y la coherencia entre lenguajes aportan valor.
| Caso | Por qué encaja gRPC | Qué pedir a Claude Code |
|---|---|---|
| Servicios internos de pedidos, inventario y facturación | El contrato reduce desalineación entre equipos | Crear .proto, servidor, cliente y tabla de códigos |
| Exportaciones grandes | La transmisión del servidor evita una respuesta gigante | Diseñar returns (stream Item) y cancelación |
| Equipos con Go, Node y Python | Un esquema puede guiar varios clientes | Documentar generación, carpetas y CI |
| Herramientas internas y agentes de IA | Los métodos explícitos reducen llamadas ambiguas | Añadir cliente de ejemplo, metadatos de autenticación, plazos y registros |
La lección práctica de Masa es que un método Search demasiado amplio parece cómodo al principio, pero se vuelve difícil de versionar. Separar lectura, creación y transmisión antes de pedir código a Claude Code deja revisiones más pequeñas.
Ejemplo gRPC en Node listo para copiar
Este ejemplo usa @grpc/proto-loader, así que no necesita protoc para la primera prueba local.
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;
});
En una terminal:
npm run server
En otra terminal:
npm run client
Evolución, seguridad y fallos
En Protocol Buffers, no reutilices números de campo. Si eliminas un campo, reserva su número y su nombre. Para nuevos datos, añade un número nuevo; si necesitas distinguir “no enviado” de “vacío”, usa presencia explícita.
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";
}
Los plazos son parte del diseño, no un detalle final. La documentación de gRPC indica que, sin plazo, un cliente puede esperar indefinidamente. La transmisión sirve para exportaciones y progreso, pero debe manejar cancelación y no acumular todo en memoria.
El ejemplo usa createInsecure() solo para la prueba local. En producción usa TLS: grpc.ServerCredentials.createSsl(...) en el servidor y grpc.credentials.createSsl(rootCert) en el cliente. Un token en metadatos no protege nada si el canal no está cifrado.
La observabilidad debe incluir método, estado, duración, cancelación, vencimiento de plazo y backend no disponible. Los indicadores gRPC de OpenTelemetry son una base práctica. No conviertas todos los errores en UNKNOWN; separa INVALID_ARGUMENT, NOT_FOUND, UNAUTHENTICATED, DEADLINE_EXCEEDED, UNAVAILABLE y RESOURCE_EXHAUSTED. Reintenta escrituras solo con clave de idempotencia.
Prompt para Claude Code
Implementa gRPC TaskService.
Condiciones:
- Crear primero proto/task.proto
- Usar Node.js, @grpc/grpc-js y @grpc/proto-loader
- Implementar CreateTask, GetTask y ListTasks con transmisión del servidor
- Añadir deadline a todos los RPC del cliente
- Validar authorization metadata
- Documentar que producción debe sustituir createInsecure por TLS
- Ejecutar npm run client y reportar la salida
Archivos editables:
- proto/task.proto
- server.js
- client.js
- package.json
Para revisión:
Revisa esta implementación gRPC con findings first.
Comprueba reutilización de números de campo, evolución del esquema,
deadline, cancellation, status code, memoria en transmisión,
TLS/auth y observability. Ordena por gravedad y cita archivos.
CTA y resultado verificado
Si estás aprendiendo solo, empieza con la chuleta gratuita de Claude Code y adapta este ejemplo en un proyecto desechable. Si tu equipo necesita gobernanza de .proto, puertas de CI, prompts de revisión, TLS y observabilidad, revisa formación y consultoría Claude Code. Para material reutilizable, usa productos ClaudeCodeLab.
Para esta actualización, el ejemplo se ejecutó en un directorio temporal con Node v24.14.1 y npm 11.11.0. npm install, node server.js y node client.js terminaron bien, y el cliente mostró created, fetched y streamed: 1. En esta máquina no estaban disponibles go, protoc ni los complementos Protobuf para Go, así que el artículo se limita al camino Node que sí pudo verificarse.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Checklist de permisos antes de que Claude Code edite un sitio de cliente
Guía para agencias que quieren usar IA en landing pages sin tocar zonas sensibles.
Convierte tickets de soporte SaaS en pasos reproducibles con Claude Code
Flujo para transformar reportes vagos en pasos, evidencia y una nota útil para ingeniería.
Convierte tus notas viejas de Obsidian en instrucciones para Claude Code en 10 minutos
Rutina de 10 minutos para separar tus notas de Obsidian en hechos, decisiones y dudas, y darle a Claude Code instrucciones que sí funcionan.