Definizione di servizi e messaggi con Protobuf IDL
Scriva contratti .proto e generi stub client e server type-safe a partire da essi.
Definizione di servizi e messaggi con Protobuf IDL è una lezione Node.js Backend Development Bootcamp gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Node.js Backend Development Bootcamp, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Node.js Backend Development Bootcamp include 4 lezioni in totale.
Parti di questa lezione non sono ancora state tradotte e vengono mostrate in inglese.
Why an IDL?
In a gRPC microservice, the contract comes before the code. You describe your data and your RPC methods in a language-neutral Interface Definition Language (IDL) called .proto, then generate type-safe stubs for Node.js, Go, Java, and more from that single file.
- One
.protofile is the single source of truth shared by client and server. - Protocol Buffers (protobuf) is both the IDL and the binary wire format.
- You never hand-write the serialization code — the compiler does it.
This lesson shows how to write .proto contracts and turn them into JavaScript client and server stubs.
Anatomy of a .proto file
Every modern .proto file starts by declaring the syntax version and a package. The package namespaces your symbols so two services can both define a User without colliding.
syntax = "proto3";— always use proto3 for new services.package— logical namespace, maps to a JS object path after generation.- The file groups
message(data shapes) andservice(RPC methods).
// user.proto
syntax = "proto3";
package users.v1;
// A data shape sent over the wire
message User {
string id = 1;
string email = 2;
bool active = 3;
}Messages and field numbers
A message is a record of typed fields. The number after the = is the field tag, not a default value. Tags are how protobuf identifies each field on the binary wire — they must be unique within a message and must never change once deployed.
- Field names can be renamed freely; tags must stay stable for backward compatibility.
- Tags 1-15 use a single byte, so reserve them for your most frequent fields.
- Scalar types:
string,bool,int32,int64,double,bytes.
message Product {
string id = 1; // tag 1 (1 byte on the wire)
string name = 2;
int32 stock = 3;
double price = 4;
bool discontinued = 5;
}proto3 to JavaScript type mapping
When you load a .proto with @grpc/proto-loader, each protobuf type maps to a JavaScript value. Knowing the mapping avoids surprises at runtime.
string,bool,int32,float,doublemap to JSstring,boolean,number.int64/uint64map to a string by default in JS (numbers can exceedNumber.MAX_SAFE_INTEGER).bytesbecomes aBuffer.- Unset proto3 scalars come back as their zero value (
"",0,false) — they are neverundefinedon the wire.
// What a decoded User object looks like in Node.js
const user = {
id: 'u_123', // string -> string
email: '', // unset string -> '' (zero value)
active: false, // unset bool -> false
loginCount: '0' // int64 -> string, not number!
};
console.log(typeof user.loginCount); // 'string'Defining a service
A service block lists the RPC methods clients can call. Each rpc takes exactly one request message and returns exactly one response message. Wrapping request/response in dedicated messages (rather than passing bare scalars) lets you add fields later without breaking the contract.
- Method names are
PascalCaseby convention. - Always define a request and a response message per method, even if one is empty.
message GetUserRequest { string id = 1; }
message GetUserResponse { User user = 1; }
service UserService {
// unary: one request -> one response
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc CreateUser(CreateUserRequest) returns (User);
}The four RPC kinds
The stream keyword on either side of an rpc declaration controls the streaming model. The same IDL keyword decides whether your generated handler receives a single value or a stream.
- Unary:
rpc Get(Req) returns (Res)— one in, one out. - Server streaming:
returns (stream Res)— server pushes many. - Client streaming:
(stream Req)— client uploads many. - Bidirectional:
(stream Req) returns (stream Res).
service OrderService {
rpc GetOrder(GetOrderRequest) returns (Order);
rpc ListOrders(ListOrdersRequest) returns (stream Order);
rpc ImportOrders(stream Order) returns (ImportSummary);
rpc LiveOrders(stream OrderEvent) returns (stream OrderEvent);
}Enums, repeated, and nested messages
Beyond scalars, protobuf gives you composite shapes for real-world data.
enum— a closed set of values; the first member must be0and is the default.repeated— an ordered list; decodes to a JSArray.- Nested messages model structured sub-records.
Prefix enum members to avoid name clashes, since enum values share the enclosing scope.
enum OrderStatus {
ORDER_STATUS_UNSPECIFIED = 0; // required zero default
ORDER_STATUS_PENDING = 1;
ORDER_STATUS_SHIPPED = 2;
}
message Order {
string id = 1;
OrderStatus status = 2;
repeated LineItem items = 3; // -> JS Array
}
message LineItem {
string sku = 1;
int32 qty = 2;
}Loading the contract in Node.js
In Node you generate stubs at runtime with @grpc/proto-loader plus @grpc/grpc-js. The loader parses the .proto and loadPackageDefinition turns it into a navigable JS object keyed by your package path.
keepCase: truepreserves field names exactly as written in the proto.longs: Stringkeeps 64-bit ints safe as strings.- The package path
users.v1becomesproto.users.v1.
const protoLoader = require('@grpc/proto-loader');
const grpc = require('@grpc/grpc-js');
const pkgDef = protoLoader.loadSync('user.proto', {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const proto = grpc.loadPackageDefinition(pkgDef);
const UserService = proto.users.v1.UserService;Implementing the server stub
The generated UserService gives you a service definition you register handlers against. Each unary handler receives (call, callback); call.request is your decoded request message, and you reply via the Node-style callback(err, response).
- Return a gRPC status by passing an error with a
codefromgrpc.status. - The response object must match the response message fields.
const server = new grpc.Server();
server.addService(UserService.service, {
GetUser(call, callback) {
const { id } = call.request;
const user = db.find(id);
if (!user) {
return callback({
code: grpc.status.NOT_FOUND,
message: `User ${id} not found`,
});
}
callback(null, { user });
},
});Calling from the client stub
The same generated UserService is also a client constructor. You instantiate it with a target address and credentials, then call methods directly. Each unary call takes the request object and a Node-style callback.
credentials.createInsecure()for local/dev; use TLS in production.- The request and response are plain JS objects matching the proto messages.
const client = new UserService(
'localhost:50051',
grpc.credentials.createInsecure()
);
client.GetUser({ id: 'u_123' }, (err, res) => {
if (err) {
console.error(err.code, err.message);
return;
}
console.log(res.user.email);
});Evolving the contract safely
A contract is only useful if it can change without breaking deployed clients. proto3 makes additive evolution safe when you follow a few rules.
- Add new fields with new, never-before-used tag numbers — old clients ignore them.
- Never reuse or renumber tags; mark removed tags with
reserved. - Renaming a field is wire-compatible (tag is what matters), but breaks JSON/text usage.
- Use
reservedfor both removed tag numbers and names to prevent accidental reuse.
message User {
reserved 4, 5; // retired tags, never reuse
reserved "phone"; // retired field name
string id = 1;
string email = 2;
bool active = 3;
string display_name = 6; // safe additive change
}Quick Check
You need to remove the deprecated phone field (tag 4) from a deployed User message. What is the correct, backward-compatible way to do it?
Recap
You learned to design and consume gRPC contracts with Protobuf IDL:
- A
.protofile withsyntax = "proto3"and apackageis the single source of truth for client and server. messagedefines typed records; the number after each field is its stable wire tag, not a default.service+rpcdeclare methods; thestreamkeyword selects unary, server-, client-, or bidirectional streaming.enum,repeated, and nested messages model richer data;int64decodes to a JS string.- In Node,
@grpc/proto-loader+grpc.loadPackageDefinitiongenerate both the server service and the client constructor. - Evolve contracts additively and protect retired tags/names with
reserved.
Domande Frequenti
La lezione «Definizione di servizi e messaggi con Protobuf IDL» è gratuita?
Sì — il testo completo di «Definizione di servizi e messaggi con Protobuf IDL» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Node.js Backend Development Bootcamp, passa a CoddyKit PRO. Il corso Node.js Backend Development Bootcamp include 4 lezioni in totale.
Cosa imparerò in «Definizione di servizi e messaggi con Protobuf IDL»?
Scriva contratti .proto e generi stub client e server type-safe a partire da essi. Eserciti Node.js Backend Development Bootcamp con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare Node.js Backend Development Bootcamp?
Non è richiesta alcuna esperienza precedente. Node.js Backend Development Bootcamp su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.
Quanto tempo richiede la lezione «Definizione di servizi e messaggi con Protobuf IDL»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione Node.js Backend Development Bootcamp?
Sì. Ogni lezione Node.js Backend Development Bootcamp include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Definizione di servizi e messaggi con Protobuf IDL
- RPC unary, streaming server, client e bidirezionale
- Interceptor, deadline e metadati
- Evoluzione dei proto e compatibilità con le versioni precedenti