Bootcamp backendontwikkeling met Node.js · Les

Interceptors, deadlines en metadata

Voeg cross-cutting-authenticatie en logging toe via interceptors, terwijl u deadlines afdwingt en metadata doorgeeft.

Les 3 van 413 stappen

Interceptors, deadlines en metadata is een gratis Bootcamp backendontwikkeling met Node.js-les op CoddyKit. Dit is les 3 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 Bootcamp backendontwikkeling met Node.js. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Bootcamp backendontwikkeling met Node.js bevat in totaal 4 lessen.

Dwarsdoorsnijdende aandachtspunten in gRPC

Naarmate je gRPC-service groeit, herhaal je dezelfde logica in elke handler: authenticatie, logboekregistratie, tijdmeting en foutvormgeving. Die logica naar elke methode kopiëren en plakken is kwetsbaar.

gRPC geeft je drie hulpmiddelen om dit netjes af te handelen:

  • Interceptors — middleware die elke aanroep omhult, aan de client- of serverkant.
  • Metadata — sleutel-waardeheaders die samen met het verzoek worden verzonden, ideaal voor authenticatietokens en verzoek-id's.
  • Deadlines — een absoluut tijdstip waarop een aanroep voltooid moet zijn en dat tussen services wordt doorgegeven.

In deze les verbinden we ze alle drie met @grpc/grpc-js in Node.js.

Wat metadata echt is

Metadata is een multimap van tekenreekssleutels naar waarden die met een gRPC-aanroep worden verzonden, vergelijkbaar met HTTP-headers. Sleutels zijn niet hoofdlettergevoelig. Waarden zijn meestal ASCII-tekenreeksen; sleutels die eindigen op -bin bevatten binaire Buffer-waarden.

Aan de clientkant voeg je metadata toe; aan de serverkant lees je die uit het call-object. Gebruik metadata voor zaken die geen deel uitmaken van de bedrijfsgegevens — authenticatietokens, trace-id's en landinstelling.

const grpc = require('@grpc/grpc-js');

// Build metadata on the client
const md = new grpc.Metadata();
md.set('authorization', 'Bearer abc123');
md.set('x-request-id', 'req-42');

// Reading is case-insensitive
console.log(md.get('Authorization')); // [ 'Bearer abc123' ]
console.log(md.get('x-request-id')); // [ 'req-42' ]

Metadata verzenden met een unaire aanroep

Elke gegenereerde clientmethode accepteert een optioneel argument van het type Metadata vóór de callback (of opties). De token die je hier instelt, komt bij de server aan voordat je handler wordt uitgevoerd.

Houd authenticatie buiten je protobuf-berichten — plaats de token in metadata, zodat dezelfde authenticatielogica voor elke RPC werkt.

const grpc = require('@grpc/grpc-js');

function callWithAuth(client, token) {
  const md = new grpc.Metadata();
  md.set('authorization', 'Bearer ' + token);

  client.GetUser({ id: '7' }, md, (err, res) => {
    if (err) return console.error('RPC failed:', err.message);
    console.log('User:', res);
  });
}

Metadata lezen op de server

Op de server stelt het call-object metadata beschikbaar via call.metadata.get(key). Dit retourneert een array, omdat een sleutel meerdere keren kan voorkomen. Lees voor authenticatie de header authorization uit en valideer die voordat je werk uitvoert.

Als de validatie mislukt, retourneer je een fout met grpc.status.UNAUTHENTICATED, zodat clients correct kunnen reageren.

const grpc = require('@grpc/grpc-js');

function getUser(call, callback) {
  const auth = call.metadata.get('authorization')[0];
  if (!auth || !auth.startsWith('Bearer ')) {
    return callback({
      code: grpc.status.UNAUTHENTICATED,
      message: 'Missing or invalid token',
    });
  }
  const token = auth.slice('Bearer '.length);
  // ... verify token, then respond
  callback(null, { id: call.request.id, name: 'Ada' });
}

Serverinterceptors: het idee

Tokens in elke handler lezen en valideren is herhalend werk. Met een serverinterceptor kun je logica één keer voor alle RPC's uitvoeren.

In @grpc/grpc-js wordt interceptie aan de serverkant aangeboden via de optie interceptors van server.addService (en via ServerInterceptingCall in nieuwere versies). Het klassieke, breed ondersteunde patroon is de clientinterceptor; aan de serverkant omhullen veel teams handlers met een kleine hogere-ordefunctie voor authenticatie en logboekregistratie.

We beginnen met dat patroon waarbij handlers worden omhuld — het is overdraagbaar en eenvoudig te testen.

Een authenticatiewrapper voor handlers

Een hogere-ordefunctie ontvangt een handler en retourneert een nieuwe handler die eerst de authenticatie controleert en daarna de oorspronkelijke handler aanroept. Dit is een nette manier zonder framework om een dwarsdoorsnijdend aandachtspunt toe te voegen.

De wrapper stopt de verwerking met UNAUTHENTICATED als de token ongeldig is; anders geeft hij de aanroep door.

const grpc = require('@grpc/grpc-js');

function withAuth(handler) {
  return (call, callback) => {
    const auth = call.metadata.get('authorization')[0];
    if (auth !== 'Bearer good-token') {
      return callback({
        code: grpc.status.UNAUTHENTICATED,
        message: 'Unauthorized',
      });
    }
    return handler(call, callback);
  };
}

// Usage when registering the service:
// server.addService(svc, { GetUser: withAuth(getUser) });

Een samenstelbare wrapper voor logboekregistratie

Hetzelfde patroon werkt voor logboekregistratie en tijdmeting. Omdat elke wrapper een handler ontvangt en een handler retourneert, kun je ze samenstellen: withLogging(withAuth(getUser)).

Hier registreert de logger het methodelabel, de latentie en het verzoek-id uit de metadata. Let erop dat dit een volledig zelfstandig programma is dat je kunt uitvoeren om de samenstelling met een nagebootst call-object in actie te zien.

function withLogging(name, handler) {
  return (call, callback) => {
    const start = Date.now();
    const reqId = call.metadata.reqId || 'none';
    handler(call, (err, res) => {
      const ms = Date.now() - start;
      console.log(`[${name}] req=${reqId} ${err ? 'ERR' : 'OK'} ${ms}ms`);
      callback(err, res);
    });
  };
}

function getUser(call, callback) {
  callback(null, { id: call.request.id, name: 'Ada' });
}

const handler = withLogging('GetUser', getUser);
handler(
  { request: { id: '7' }, metadata: { reqId: 'req-42' } },
  (err, res) => console.log('Response:', res)
);

Clientinterceptors

Het officiële uitbreidingspunt aan de clientkant is de interceptor: een functie die options en een nextCall ontvangt en een InterceptingCall retourneert. Je overschrijft levenscyclusmethoden zoals start om metadata toe te voegen, of onReceiveStatus om resultaten te observeren.

Hieronder krijgt elke uitgaande aanroep automatisch een header authorization — aanroepers hoeven daar nooit zelf aan te denken.

const grpc = require('@grpc/grpc-js');

function authInterceptor(token) {
  return (options, nextCall) =>
    new grpc.InterceptingCall(nextCall(options), {
      start(metadata, listener, next) {
        metadata.set('authorization', 'Bearer ' + token);
        next(metadata, listener);
      },
    });
}

// const client = new UserService(addr, creds, {
//   interceptors: [authInterceptor('abc123')],
// });

Deadlines begrijpen

Een deadline is een absoluut tijdstip waarop een aanroep voltooid moet zijn. Het is GEEN time-out per tussenstap — het is een moment op de klok dat stroomafwaarts wordt doorgegeven, zodat een keten van services één gedeeld tijdsbudget gebruikt.

Stel de deadline in bij de options van de aanroep als deadline: een Date of milliseconden sinds het epoch. Het gebruikelijke patroon is "nu plus N ms".

  • Als de deadline verstrijkt, mislukt de aanroep met DEADLINE_EXCEEDED (status 4).
  • Servers kunnen de resterende tijd controleren en het werk vroegtijdig afbreken.
const grpc = require('@grpc/grpc-js');

function callWithDeadline(client) {
  const deadline = new Date(Date.now() + 2000); // 2s budget
  client.GetUser({ id: '7' }, { deadline }, (err, res) => {
    if (err && err.code === grpc.status.DEADLINE_EXCEEDED) {
      return console.error('Timed out');
    }
    if (err) return console.error(err.message);
    console.log(res);
  });
}

Deadlines aan de serverkant

De server ontvangt de deadline via call.getDeadline(). Bereken voordat je duur werk start het resterende budget en stop vroegtijdig als dat al op is — zo verspil je geen CPU aan een aanroep die de client heeft opgegeven.

Wanneer je aanroepen naar stroomafwaartse services verspreidt, geef je DEZELFDE deadline door, zodat de hele boom één budget respecteert.

const grpc = require('@grpc/grpc-js');

function slowHandler(call, callback) {
  const deadline = call.getDeadline(); // ms or Date
  const remaining = Number(deadline) - Date.now();
  if (remaining <= 0) {
    return callback({ code: grpc.status.DEADLINE_EXCEEDED, message: 'No time left' });
  }
  // Forward the same deadline downstream:
  // downstream.Fetch(req, { deadline }, cb);
  callback(null, { ok: true, budgetMs: remaining });
}

Alles samenbrengen: een pijplijn met budget

Dit is het mentale model voor één verzoek: de client stelt een deadline en authenticatiemetadata in. Een clientinterceptor voegt de token toe; de aanroep bevat een verzoek-id. Op de server valideert een authenticatiewrapper de gegevens, meet een logboekwrapper de tijd en controleren handlers de resterende deadline voordat ze aanroepen verspreiden — waarbij zowel metadata als de deadline stroomafwaarts worden doorgegeven.

Deze zelfstandige simulatie toont de volledige keten van wrappers en een deadlinecontrole zonder server, zodat je haar rechtstreeks kunt uitvoeren.

function withAuth(h) {
  return (call, cb) =>
    call.metadata.authorization === 'Bearer good'
      ? h(call, cb)
      : cb({ code: 16, message: 'UNAUTHENTICATED' });
}
function withDeadline(h) {
  return (call, cb) =>
    call.deadline - Date.now() <= 0
      ? cb({ code: 4, message: 'DEADLINE_EXCEEDED' })
      : h(call, cb);
}
function getUser(call, cb) {
  cb(null, { id: call.request.id, name: 'Ada' });
}

const pipeline = withAuth(withDeadline(getUser));
const call = {
  request: { id: '7' },
  metadata: { authorization: 'Bearer good' },
  deadline: Date.now() + 1000,
};
pipeline(call, (err, res) =>
  console.log(err ? 'Error ' + err.code : 'OK', res || '')
);

Korte controle

Een client stelt deadline = Date.now() + 3000 in en roept service A aan, die vervolgens service B aanroept. Wat is het juiste gedrag voor de deadline?

Samenvatting

Je beschikt nu over de gereedschappen voor gRPC-aanroepen op productiekwaliteitsniveau:

  • Metadata bevat gegevens buiten het bericht, zoals authorization en x-request-id; lees deze aan de serverkant met call.metadata.get(key) (dit retourneert een array en sleutels zijn niet hoofdlettergevoelig).
  • Interceptors centraliseren dwarsdoorsnijdende logica: clientinterceptors gebruiken InterceptingCall om metadata toe te voegen in start; aan de serverkant voegen samenstelbare handlerwrappers authenticatie en logboekregistratie één keer toe.
  • Deadlines zijn absolute tijdstippen die via options.deadline van de aanroep worden ingesteld en ongewijzigd stroomafwaarts worden doorgegeven, zodat een hele keten één budget deelt; als de deadline wordt overschreden, ontstaat DEADLINE_EXCEEDED (status 4).

Combineer ze: voeg authenticatie en een verzoek-id toe via een interceptor, omhul handlers voor authenticatie en logboekregistratie en geef de deadline altijd door wanneer je aanroepen verspreidt.

Gratis beginnen

Leer JavaScript 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
22
Lessen
92

Veelgestelde vragen

Is de les “Interceptors, deadlines en metadata” gratis?

Ja — de volledige tekst van “Interceptors, deadlines en metadata” 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 Bootcamp backendontwikkeling met Node.js wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus Bootcamp backendontwikkeling met Node.js bevat in totaal 4 lessen.

Wat leer ik in “Interceptors, deadlines en metadata”?

Voeg cross-cutting-authenticatie en logging toe via interceptors, terwijl u deadlines afdwingt en metadata doorgeeft. Je oefent met Bootcamp backendontwikkeling met Node.js 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 Bootcamp backendontwikkeling met Node.js te beginnen?

Ervaring vooraf is niet nodig. Bootcamp backendontwikkeling met Node.js 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 3 van 4.

Hoe lang duurt de les “Interceptors, deadlines en metadata”?

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 Bootcamp backendontwikkeling met Node.js?

Ja. Elke les over Bootcamp backendontwikkeling met Node.js 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 met Protobuf IDL
  2. Unary-, server-, client- en bidirectionele streaming-RPC's
  3. Interceptors, deadlines en metadata
  4. Evolutie van proto’s en achterwaartse compatibiliteit
← Terug naar Bootcamp backendontwikkeling met Node.js