Enterprise-backend-API's met NestJS · Les

Services en berichten definiëren in Protobuf

Maak .proto-contracten en genereer getypeerde interfaces voor NestJS-microservices.

Les 1 van 413 stappen

Services en berichten definiëren in Protobuf is een gratis Enterprise-backend-API's met NestJS-les op CoddyKit. Dit is les 1 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Enterprise-backend-API's met NestJS. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Enterprise-backend-API's met NestJS bevat in totaal 4 lessen.

Waarom Protobuf het contract bepaalt

In een NestJS gRPC-microservice is het .proto-bestand de enkele bron van waarheid. Het definieert de indeling op de draad, het RPC-oppervlak en — zodra het is gecompileerd — de TypeScript-typen die zowel de client als de server delen.

  • Berichten beschrijven de gegevensvormen die over de draad worden verstuurd.
  • Diensten declareren de aanroepbare RPC-methoden en hun aanvraag- en antwoordberichten.

In tegenstelling tot REST + OpenAPI (waar het schema vaak na de code wordt geschreven), schrijf je bij gRPC eerst het contract en genereer je daaruit de code. Dit is een contractgestuurd ontwerp.

Anatomie van een .proto-bestand

Elk contract begint met het declareren van de syntaxisversie en een package. Het pakket wordt een naamruimte die NestJS gebruikt om je dienst tijdens runtime te vinden.

  • syntax = "proto3"; — gebruik voor moderne gRPC altijd proto3.
  • package billing; — de naamruimte waarnaar wordt verwezen in de transportopties van NestJS.

Hieronder staat een minimaal maar volledig contract voor een factureringsdienst.

// proto/billing.proto
syntax = "proto3";

package billing;

service InvoiceService {
  rpc GetInvoice (GetInvoiceRequest) returns (Invoice);
}

message GetInvoiceRequest {
  string id = 1;
}

message Invoice {
  string id = 1;
  string customer_id = 2;
  int64 amount_cents = 3;
  string currency = 4;
}

Veldnummers zijn het contract

Elk veld heeft een tagnummer (de = 1, = 2...). Deze nummers — niet de veldnamen — worden op de draad gecodeerd.

  • Gebruik een bestaand veld nooit opnieuw en nummer het nooit opnieuw; daardoor verbreek je de binaire compatibiliteit met uitgerolde clients.
  • Tags 1–15 gebruiken één byte; reserveer ze voor de velden die het vaakst worden verzonden.
  • Je mag een veld veilig hernoemen (de naam is lokaal voor de gegenereerde code), maar wijzig het nummer of type nooit.

Wanneer je een veld verwijdert, markeer je het nummer als reserved, zodat het nooit per ongeluk opnieuw wordt gebruikt.

message Invoice {
  reserved 5, 6;
  reserved "legacy_tax_field";

  string id = 1;
  string customer_id = 2;
  int64 amount_cents = 3;
  string currency = 4;
}

Scalaire typen en de int64-valkuil

Proto3-scalars worden aan TypeScript gekoppeld, maar die koppeling heeft scherpe randen voor bedrijfs-API's die geldbedragen of ID's verwerken.

  • string → string, bool → boolean, int32/float/double → number.
  • int64, uint64, fixed64 → worden door de meeste loaders weergegeven als string (of Long), omdat JavaScript-getallen boven 2^53 precisie verliezen.

Voor amount_cents als int64 moet je gegenereerde interface het behandelen als een string, zodat grote waarden niet stilzwijgend worden afgerond.

// Generated-style interface for the Invoice message
export interface Invoice {
  id: string;
  customerId: string;
  // int64 surfaces as string to preserve precision
  amountCents: string;
  currency: string;
}

snake_case erin, camelCase eruit

De Protobuf-conventie gebruikt snake_case voor veldnamen. De NestJS gRPC-loader (@grpc/proto-loader) gebruikt standaard keepCase: false, waardoor velden in de gegenereerde objecten en runtime-objecten worden omgezet naar camelCase.

  • customer_id in .proto wordt customerId in TypeScript.
  • Als je keepCase: true instelt, moet je customer_id letterlijk lezen — dit veroorzaakt vaak fouten met undefined.

Kies per project één conventie en configureer de loader consequent.

// main.ts transport options
import { Transport, GrpcOptions } from '@nestjs/microservices';
import { join } from 'path';

export const grpcOptions: GrpcOptions = {
  transport: Transport.GRPC,
  options: {
    package: 'billing',
    protoPath: join(__dirname, 'proto/billing.proto'),
    loader: { keepCase: false, longs: String, enums: String },
  },
};

Enumeraties en hun nulwaarderegel

Proto3-enumeraties moeten een nulwaarde als eerste item definiëren — dit is de impliciete standaardwaarde wanneer een veld op de draad niet is ingesteld.

  • Noem de nulwaarde *_UNSPECIFIED, zodat een niet-ingestelde waarde te onderscheiden is van de "eerste echte toestand".
  • Enumeraties zijn open in proto3: een client met een nieuwer schema kan een getal verzenden dat je server niet kent, dus verwerk altijd de standaardtak.

Met enums: String in de loader komen waarden in TypeScript aan als hun tekenreeksnamen.

enum InvoiceStatus {
  INVOICE_STATUS_UNSPECIFIED = 0;
  INVOICE_STATUS_DRAFT = 1;
  INVOICE_STATUS_SENT = 2;
  INVOICE_STATUS_PAID = 3;
  INVOICE_STATUS_VOID = 4;
}

message Invoice {
  string id = 1;
  InvoiceStatus status = 5;
}

De service-interface in NestJS

Elke rpc-methode wordt een methode op een gegenereerde TypeScript-interface. Unaire RPC's retourneren in NestJS een Observable (of Promise) van het antwoordbericht.

Meestal schrijf of genereer je zelf een interface en injecteer je de client via ClientGrpc.getService().

import { Observable } from 'rxjs';

export interface GetInvoiceRequest {
  id: string;
}

export interface InvoiceServiceClient {
  getInvoice(request: GetInvoiceRequest): Observable<Invoice>;
}

export interface Invoice {
  id: string;
  customerId: string;
  amountCents: string;
  currency: string;
  status: string;
}

De servicehandler implementeren

Aan de serverkant versier je een controllermethode met @GrpcMethod. Het eerste argument is de servicenaam uit de .proto, het tweede is de naam van de rpc-methode.

  • De methode ontvangt het gedecodeerde aanvraagbericht als een gewoon object.
  • Retourneer rechtstreeks de vorm van het antwoordbericht, of een Observable/Promise daarvan.
import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';

@Controller()
export class InvoiceController {
  @GrpcMethod('InvoiceService', 'GetInvoice')
  getInvoice(data: { id: string }): Invoice {
    return {
      id: data.id,
      customerId: 'cust_42',
      amountCents: '120000',
      currency: 'EUR',
      status: 'INVOICE_STATUS_SENT',
    };
  }
}

Geneste berichten en compositie

Berichten kunnen worden samengesteld. Een veld kan een ander berichttype zijn, zodat je rijke aggregaten kunt modelleren zonder alles tot één vorm plat te slaan.

  • Verwijs naar een berichttype met de naam ervan; definieer het ervoor of erna — de volgorde binnen een bestand maakt niet uit.
  • Een niet-ingesteld genest bericht komt in TypeScript aan als undefined, dus controleer dit voordat je de velden ervan benadert.
message Money {
  int64 amount_cents = 1;
  string currency = 2;
}

message LineItem {
  string sku = 1;
  uint32 quantity = 2;
  Money unit_price = 3;
}

message Invoice {
  string id = 1;
  string customer_id = 2;
  Money total = 3;
  repeated LineItem line_items = 4;
}

Herhaalde velden, maps en streaming-RPC's

Met deze twee extra bouwstenen zijn de meeste bedrijfscontracten compleet:

  • repeated T field = N; → een TypeScript-array T[]. Een lege lijst en een niet-ingestelde lijst zijn op de draad niet van elkaar te onderscheiden.
  • map<string, T> → een TypeScript-record; nuttig voor labels en metagegevens.

Voeg stream toe aan de aanvraag, het antwoord of beide om serverstreaming, clientstreaming of bidirectionele RPC's te declareren. In NestJS gebruiken streamingmethoden @GrpcStreamMethod en werken ze met RxJS-Observable-streams.

service InvoiceService {
  rpc GetInvoice (GetInvoiceRequest) returns (Invoice);
  // server-streaming: many invoices for one query
  rpc ListInvoices (ListInvoicesRequest) returns (stream Invoice);
}

message ListInvoicesRequest {
  string customer_id = 1;
  map<string, string> filters = 2;
}

Getypeerde interfaces genereren met ts-proto

Handmatig geschreven interfaces raken los van de .proto. Gebruik een generator zoals ts-proto (via protoc) om interfaces, helpers voor coderen en decoderen en een NestJS-vriendelijke client-/servicevorm te genereren die synchroon blijft met het contract.

  • nestJs=true genereert de gRPC-service-/clientinterfaces die NestJS verwacht.
  • Voer dit uit in CI, zodat een verouderd gegenereerd bestand de build laat mislukken en code gegarandeerd overeenkomt met het contract.
// package.json script
{
  "scripts": {
    "proto:gen": "protoc --plugin=node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=./src/generated --ts_proto_opt=nestJs=true,outputServices=grpc-js,useDate=false ./proto/billing.proto"
  }
}

Korte controle: het contract uitbreiden

Je hebt Invoice uitgebracht met string customer_id = 2;. Een nieuwe vereiste vraagt dat je niet langer customer_id verzendt, maar in plaats daarvan het rijkere geneste bericht Customer customer = 6;. Uitgerolde oudere clients moeten blijven werken.

Samenvatting: contractgestuurde Protobuf

Je kunt nu een servicecontract schrijven en dit omzetten in getypeerde NestJS-interfaces:

  • Structuur: syntax = proto3, een package-naamruimte, message-vormen en een service met rpc-methoden.
  • Veldnummers vormen het draadcontract — evolueer alleen door toevoegingen; reserveer met reserved buiten gebruik gestelde nummers en namen.
  • Typekoppeling: let op int64 → string, snake_case → camelCase; enumeraties hebben een nulwaarde *_UNSPECIFIED nodig.
  • Compositie: geneste berichten, repeated-arrays, map en stream-RPC's.
  • Generatie: leid typen in CI met ts-proto af uit de .proto, zodat code nooit kan afwijken van het contract.
Gratis beginnen

Leer TypeScript met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
20
Lessen
76

Veelgestelde vragen

Is de les “Services en berichten definiëren in Protobuf” gratis?

Ja — de volledige tekst van “Services en berichten definiëren in Protobuf” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus Enterprise-backend-API's met NestJS wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus Enterprise-backend-API's met NestJS bevat in totaal 4 lessen.

Wat leer ik in “Services en berichten definiëren in Protobuf”?

Maak .proto-contracten en genereer getypeerde interfaces voor NestJS-microservices. Je oefent met Enterprise-backend-API's met NestJS door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Enterprise-backend-API's met NestJS te beginnen?

Ervaring vooraf is niet nodig. Enterprise-backend-API's met NestJS op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 1 van 4.

Hoe lang duurt de les “Services en berichten definiëren in Protobuf”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Enterprise-backend-API's met NestJS?

Ja. Elke les over Enterprise-backend-API's met NestJS bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Services en berichten definiëren in Protobuf
  2. gRPC-methoden implementeren en gebruiken
  3. Streaming-RPC's en backpressure
  4. Contractevolutie en achterwaartse compatibiliteit
← Terug naar Enterprise-backend-API's met NestJS