Développement gRPC avec Claude Code : Protobuf, flux et exploitation
Créez un service gRPC exécutable avec Claude Code, Protobuf, délais, flux, auth et observabilité.
Commencer par le contrat
gRPC est un cadre d’appel distant : le client appelle une méthode d’un autre service comme une fonction locale, mais l’échange passe par le réseau. Protocol Buffers définit le contrat : services, méthodes, messages de requête, messages de réponse et numéros de champ. Avec Claude Code, ce contrat doit être fixé avant le serveur et le client.
Gardez les références officielles ouvertes : gRPC Introduction, Core concepts, Node basics, Deadlines, Status Codes, Authentication, OpenTelemetry Metrics et le guide Protocol Buffers proto3.
Pour compléter, lisez aussi développement API en production, microservices avec Claude Code, stratégie de versionnement API et stratégies de test.
Cas d’usage concrets
gRPC n’est pas seulement un choix de performance. Il devient utile quand le contrat typé, les erreurs explicites et la cohérence entre langages réduisent le risque.
| Cas d’usage | Pourquoi gRPC convient | Travail à confier à Claude Code |
|---|---|---|
| Services internes de commande, stock, facturation | Le contrat limite les dérives entre équipes | Rédiger .proto, serveur, client et table des statuts |
| Export massif | Le flux serveur évite une réponse énorme | Implémenter returns (stream Item) et l’annulation |
| Équipe Go, Node, Python | Un schéma aligne plusieurs clients | Documenter génération, dossiers et CI |
| Outils internes et agents IA | Des méthodes explicites évitent les appels flous | Ajouter client d’exemple, métadonnées d’auth, délais et journaux |
Le retour d’expérience de Masa est simple : une méthode Search trop large semble pratique, puis devient difficile à faire évoluer. Séparez lecture, création et flux avant de demander à Claude Code d’écrire beaucoup de fichiers.
Exemple gRPC Node exécutable
Cet exemple utilise @grpc/proto-loader, donc la première vérification locale ne demande pas 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;
});
Lancez le serveur :
npm run server
Puis le client :
npm run client
Évolution, sécurité et modes de panne
Dans Protocol Buffers, le piège principal est la réutilisation des numéros de champ. Ne changez pas un numéro déjà publié. Si vous supprimez un champ, réservez son numéro et son nom.
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";
}
Les délais doivent être explicites. La documentation gRPC indique qu’un client sans délai peut attendre indéfiniment. Le flux serveur est pratique pour les exports, mais il doit gérer l’annulation et éviter de charger tout le résultat en mémoire.
L’exemple local utilise createInsecure() uniquement pour rester simple à copier. En production, utilisez TLS avec grpc.ServerCredentials.createSsl(...) côté serveur et grpc.credentials.createSsl(rootCert) côté client. Un jeton dans les métadonnées ne suffit pas si le canal n’est pas chiffré.
L’observabilité doit couvrir méthode, statut, durée, annulation, expiration de délai et indisponibilité du backend. Les métriques gRPC OpenTelemetry donnent une bonne base. Ne retournez pas tout en UNKNOWN : distinguez INVALID_ARGUMENT, NOT_FOUND, UNAUTHENTICATED, DEADLINE_EXCEEDED, UNAVAILABLE et RESOURCE_EXHAUSTED. Les appels d’écriture ne doivent être réessayés automatiquement qu’avec une clé d’idempotence.
Demande à envoyer à Claude Code
Implémente TaskService en gRPC.
Contraintes:
- Créer d'abord proto/task.proto
- Utiliser Node.js, @grpc/grpc-js et @grpc/proto-loader
- Implémenter CreateTask, GetTask et ListTasks en flux serveur
- Ajouter un deadline à chaque RPC client
- Vérifier authorization metadata
- Documenter que la production doit remplacer createInsecure par TLS
- Exécuter npm run client et rapporter la sortie
Fichiers modifiables:
- proto/task.proto
- server.js
- client.js
- package.json
Pour la revue :
Revois cette implémentation gRPC en findings first.
Vérifie réutilisation des numéros de champ, évolution du schéma,
deadline, cancellation, status code, mémoire du flux,
TLS/auth et observability. Classe par gravité avec fichiers précis.
CTA et résultat de vérification
Si vous apprenez seul, commencez avec la fiche gratuite Claude Code et adaptez cet exemple dans un projet jetable. Pour une équipe qui doit gérer .proto, CI, prompts de revue, TLS et observabilité, la suite naturelle est formation et conseil Claude Code. Les supports réutilisables sont dans les produits ClaudeCodeLab.
Pour cette mise à jour, l’exemple a été exécuté dans un dossier temporaire avec Node v24.14.1 et npm 11.11.0. npm install, node server.js et node client.js ont réussi, avec une sortie contenant created, fetched et streamed: 1. go, protoc et les greffons Protobuf Go n’étaient pas disponibles sur cette machine ; l’article utilise donc le chemin Node vérifié localement.
PDF gratuit: cheatsheet Claude Code
Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.
Nous protégeons vos données et n'envoyons pas de spam.
À propos de l'auteur
Masa
Ingénieur spécialisé dans les workflows pratiques avec Claude Code.
Articles liés
Workflow Obsidian vers CLAUDE.md avec Claude Code
Transformer des notes Obsidian en notes CLAUDE.md concises pour reprendre les sessions sans réexpliquer.
Claude Code Revenue CTA Routing : relier articles, PDF, Gumroad et consultation
Un workflow Claude Code pour orienter les lecteurs vers PDF gratuit, Gumroad ou consultation selon l'intention.
Règles de handoff Claude Code en équipe: preuves, permissions, rollback et revenus
Un format concret pour transmettre un travail Claude Code avec preuves, permissions, rollback, PDF gratuit, Gumroad et consultation.