Bootcamp i backendudvikling med Node.js · Lektion

Definition af services og beskeder med Protobuf IDL

Skriv .proto-kontrakter, og generér typesikre klient- og serverstubs ud fra dem.

Lektion 1 af 413 trin

Definition af services og beskeder med Protobuf IDL er en gratis Bootcamp i backendudvikling med Node.js-lektion på CoddyKit. Dette er lektion 1 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Bootcamp i backendudvikling med Node.js, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Bootcamp i backendudvikling med Node.js-kurset indeholder 4 lektioner i alt.

Hvorfor et IDL?

I en gRPC-mikrotjeneste kommer kontrakten før koden. Du beskriver dine data og dine RPC-metoder i et sproguafhængigt Interface Definition Language (IDL), der kaldes .proto, og genererer derefter typesikre stubber til Node.js, Go, Java og flere ud fra den ene fil.

  • Én .proto-fil er den fælles sandhedskilde for klienten og serveren.
  • Protocol Buffers (protobuf) er både IDL'et og det binære wire-format.
  • Du skriver aldrig serialiseringskoden i hånden — compileren gør det.

I denne lektion lærer du at skrive .proto-kontrakter og omdanne dem til JavaScript-klient- og serverstubber.

En .proto-fils opbygning

Alle moderne .proto-filer begynder med at erklære syntaks-versionen og en pakke. package opretter et navnerum for dine symboler, så to tjenester begge kan definere en User uden navnekonflikter.

  • syntax = "proto3"; — brug altid proto3 til nye tjenester.
  • package — logisk navnerum, som efter generering svarer til en JS-objektsti.
  • Filen samler message (datastrukturer) og service (RPC-metoder).
// 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;
}

Meddelelser og feltnumre

En message er en post med typede felter. Tallet efter = er feltets tag, ikke en standardværdi. Tags bruges af protobuf til at identificere hvert felt på det binære wire-format — de skal være unikke i en meddelelse og må aldrig ændres, når de først er taget i brug.

  • Feltnavne kan omdøbes frit; tags skal forblive uændrede af hensyn til bagudkompatibilitet.
  • Tags fra 1 til 15 bruger én byte, så reserver dem til dine hyppigst anvendte felter.
  • Skalartyper: 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;
}

Tilknytning fra proto3 til JavaScript-typer

Når du indlæser en .proto-fil med @grpc/proto-loader, knyttes hver protobuf-type til en JavaScript-værdi. Hvis du kender denne tilknytning, undgår du overraskelser under kørsel.

  • string, bool, int32, float og double knyttes til JS-typerne string, boolean og number.
  • int64 / uint64 knyttes som standard til en streng i JS (tal kan overstige Number.MAX_SAFE_INTEGER).
  • bytes bliver til en Buffer.
  • Uangivne proto3-skalartyper kommer tilbage som deres nulværdi ("", 0, false) — de er aldrig undefined på wire-formatet.
// 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'

Definition af en tjeneste

En service-blok viser de RPC-metoder, som klienter kan kalde. Hver rpc tager præcis én request-meddelelse og returnerer præcis én response-meddelelse. Hvis du pakker request og response ind i dedikerede meddelelser i stedet for at sende rå skalartyper, kan du senere tilføje felter uden at bryde kontrakten.

  • Metodenavne skrives efter konventionen i PascalCase.
  • Definér altid en request- og en response-meddelelse for hver metode, også selvom den ene er tom.
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);
}

De fire RPC-typer

Nøgleordet stream på hver side af en rpc-erklæring styrer streamingmodellen. Det samme IDL-nøgleord afgør, om din genererede handler modtager en enkelt værdi eller en stream.

  • Unary: rpc Get(Req) returns (Res) — én ind, én ud.
  • Serverstreaming: returns (stream Res) — serveren sender mange.
  • Klientstreaming: (stream Req) — klienten uploader mange.
  • Tovejsstreaming: (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 og indlejrede meddelelser

Ud over skalartyper giver protobuf dig sammensatte datastrukturer til virkelige data.

  • enum — et afgrænset sæt værdier; det første medlem skal være 0 og er standardværdien.
  • repeated — en ordnet liste, som afkodes til et JS-Array.
  • Indlejrede meddelelser modellerer strukturerede underposter.

Sæt præfiks på enum-medlemmer for at undgå navnekonflikter, fordi enum-værdier deler det omgivende navnerum.

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;
}

Indlæsning af kontrakten i Node.js

I Node genererer du stubber under kørsel med @grpc/proto-loader sammen med @grpc/grpc-js. Loaderen fortolker .proto-filen, og loadPackageDefinition omdanner den til et navigerbart JS-objekt, der bruger din pakkesti som nøgler.

  • keepCase: true bevarer feltnavnene præcis, som de er skrevet i proto-filen.
  • longs: String holder 64-bit-heltal sikre som strenge.
  • Pakkestien users.v1 bliver til proto.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;

Implementering af serverstubben

Den genererede UserService giver dig en tjenestedefinition, som du registrerer handlers til. Hver unary-handler modtager (call, callback); call.request er din afkodede request-meddelelse, og du svarer via den Node-lignende callback(err, response).

  • Returnér en gRPC-status ved at sende en fejl med en code fra grpc.status.
  • Response-objektet skal matche felterne i response-meddelelsen.
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 });
  },
});

Kald fra klientstubben

Den samme genererede UserService er også en klientkonstruktør. Du opretter en instans med en måladresse og legitimationsoplysninger og kalder derefter metoder direkte. Hvert unary-kald tager request-objektet og en Node-lignende callback.

  • credentials.createInsecure() til lokal udvikling; brug TLS i produktion.
  • Request og response er almindelige JS-objekter, der matcher proto-meddelelserne.
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);
});

Sikker videreudvikling af kontrakten

En kontrakt er kun nyttig, hvis den kan ændres uden at ødelægge klienter, der allerede er sat i drift. proto3 gør tilføjende videreudvikling sikker, når du følger nogle få regler.

  • Tilføj nye felter med nye, aldrig før anvendte tagnumre — gamle klienter ignorerer dem.
  • Genbrug eller omnummerér aldrig tags; markér fjernede tags med reserved.
  • Omdøbning af et felt er kompatibel med wire-formatet (det er tagget, der betyder noget), men bryder brug af JSON og tekst.
  • Brug reserved til både fjernede tagnumre og navne for at forhindre utilsigtet genbrug.
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
}

Hurtigt tjek

Du skal fjerne det forældede phone-felt (tag 4) fra en User-meddelelse, der allerede er sat i drift. Hvad er den korrekte, bagudkompatible måde at gøre det på?

Opsummering

Du har lært at designe og bruge gRPC-kontrakter med Protobuf IDL:

  • En .proto-fil med syntax = "proto3" og en package er den fælles sandhedskilde for klient og server.
  • message definerer typede poster; tallet efter hvert felt er det stabile wire-tag, ikke en standardværdi.
  • service + rpc erklærer metoder; nøgleordet stream vælger unary-, server-, klient- eller tovejsstreaming.
  • enum, repeated og indlejrede meddelelser modellerer mere avancerede data; int64 afkodes til en JS-streng.
  • I Node genererer @grpc/proto-loader + grpc.loadPackageDefinition både servertjenesten og klientkonstruktøren.
  • Videreudvikl kontrakter ved at tilføje felter, og beskyt pensionerede tags og navne med reserved.
Gratis at komme i gang

Lær JavaScript med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
22
Lektioner
92

Ofte stillede spørgsmål

Er lektionen “Definition af services og beskeder med Protobuf IDL” gratis?

Ja — hele teksten til “Definition af services og beskeder med Protobuf IDL” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Bootcamp i backendudvikling med Node.js-kurset, skal du opgradere til CoddyKit PRO. Bootcamp i backendudvikling med Node.js-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Definition af services og beskeder med Protobuf IDL”?

Skriv .proto-kontrakter, og generér typesikre klient- og serverstubs ud fra dem. Du øver dig i Bootcamp i backendudvikling med Node.js med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Bootcamp i backendudvikling med Node.js?

Der kræves ingen tidligere erfaring. Bootcamp i backendudvikling med Node.js på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 1 af 4.

Hvor lang tid tager lektionen “Definition af services og beskeder med Protobuf IDL”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Bootcamp i backendudvikling med Node.js-lektion?

Ja. Alle Bootcamp i backendudvikling med Node.js-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Definition af services og beskeder med Protobuf IDL
  2. Unary-, server-, client- og bidirectional-streaming-RPC'er
  3. Interceptors, deadlines og metadata
  4. Udvikling af protoer og bagudkompatibilitet
← Tilbage til Bootcamp i backendudvikling med Node.js