Middleware et Events

Étendez Transport, Endpoint, Service et les modules collaboratifs avec des Middleware et Events généraux.

Middleware et Events sont les points d’extension généraux du Collaboration SDK. L’identité et les autorisations passent par les Middleware. Utilisez-les pour valider et restreindre avant une opération, et les Events pour consigner les changements ou lancer des suites après confirmation de l’état. Les deux servent aussi aux logs, au traçage, aux métriques et à l’état dérivé.

Deux moments d’extension

MiddlewareEvent
ExécutionPendant une opérationAprès confirmation d’un changement d’état
Participation ou refus de l’opérationOuiNon
Enrichissement du ContextOuiLit le Context et le résultat portés par l’Event
Échec d’un écouteurPeut faire échouer l’opération couranteIsolé du résultat métier déjà déterminé
Usages courantsIdentité, autorisation, validation, logs, chronométrageIndex dérivés, caches, métriques, notifications dans le processus

Utilisez un Middleware pour décider si une opération peut continuer, un Event pour observer un changement déjà survenu.

Modèle d’exécution des Middleware

Les Middleware d’une même Action forment une chaîne asynchrone dans l’ordre d’inscription. await next() entre dans le Middleware suivant ; l’opération SDK s’exécute au point le plus interne :

TypeScript
service.use("submitChangeset", async (context, next) => {  const startedAt = performance.now();  context.customData.startedAt = startedAt;  try {    await next();  } finally {    metrics.observe("collaboration.submit.duration", performance.now() - startedAt);  }});

Les Middleware peuvent :

  • lire la requête propre à l’Action ;
  • partager les données de l’appel via customData ;
  • inspecter l’entrée ou interroger l’application avant next() ;
  • consigner durée ou résultat après le retour de next() ;
  • lever CollabError pour refuser une opération collaborative attendue.

Un Middleware HTTP Transport peut terminer la réponse sans next(). Les Middleware Service et Endpoint doivent refuser par une erreur explicite plutôt que d’omettre silencieusement next().

Middleware Transport

transport.use() enregistre les Middleware HTTP ordinaires, exécutés dans l’ordre d’enregistrement. Appelez await next() pour poursuivre le traitement, ou terminez directement la réponse. transport.useUpgrade() traite les négociations WebSocket.

Par exemple, mesurez la durée HTTP, authentifiez la requête et définissez un userID fiable, puis enregistrez le Collaboration Endpoint :

TypeScript
transport.use(async (context, next) => {  const start = performance.now();  context.response.once("finish", () => {    recordHttpDuration({      method: context.incomingMessage.method,      route: context.route?.path ?? "unmatched",      status: context.response.statusCode,      durationMs: performance.now() - start,    });  });  await next();});transport.use(async (context, next) => {  const user = await authenticate(context.incomingMessage);  if (!user) {    context.response.statusCode = 401;    context.response.end("Authentication required");    return;  }  context.userID = user.userId;  await next();});transport.register(collaborationEndpoint);

L’application fournit recordHttpDuration et authenticate. L’Endpoint utilise le userID défini par les Middleware précédents. Le Middleware d’observation précède l’authentification afin d’enregistrer également les refus.

context.route?.path est le modèle complet de la route correspondante, disponible lorsque l’exécution atteint l’Endpoint. Utilisez l’événement finish de la réponse pour mesurer le temps jusqu’à sa fin ; les refus avant la résolution de la route et les chemins inconnus sont enregistrés comme unmatched.

Middleware Endpoint

Ils participent aux opérations de Session temps réel et de Presence :

ActionMomentUsages courants
connectAprès consommation du ticket et création de Session, avant HELLOPolitique de connexion, nom et avatar du membre
joinUnitAvant l’entrée d’une Session dans une Room UnitAdmission et visibilité de l’Unit
receivePresenceAprès réception de Presence d’une Session inscrite, avant diffusionValidation et filtrage de Presence
sendPresenceAvant l’envoi de Presence à chaque membreFiltrage par destinataire
TypeScript
endpoint.use("receivePresence", async (context, next) => {  if (JSON.stringify(context.payload).length > 8_000) {    throw new CollabError("INVALID_REQUEST", "Presence payload is too large");  }  await next();});

session.userID et session.customData de l’Endpoint viennent de la requête HTTP ayant émis le Session Ticket. L’Endpoint crée memberID pour la Session WebSocket courante ; il change à la reconnexion.

Middleware Service

Ils participent au cycle de vie des données collaboratives faisant autorité :

ActionFrontièreUsages courants
readUnitDataAvant lecture d’instantanés, blocs ou modificationsLecture, contrôle de locataire, audit
createUnitAvant création atomique d’UnitTypes, politique de création, règles métier
deleteUnitsAvant écriture du lot de suppressionPolitique de suppression et contrôle par Unit
recoverUnitsAvant écriture du lot de restaurationPolitique de restauration et audit
submitChangesetAvant idempotence et OT de la soumission logiqueÉdition, limite de taille totale, traçage
applyChangesetAvant application des changements transformés à l’état candidatValidation par mutation
commitChangesetAvant CAS de révision et commit de l’AdapterRègles finales de révision et métriques
TypeScript
service.use("createUnit", async (context, next) => {  await creationPolicy.assertAllowed({    userID: context.userID,    unitID: context.request.snapshot.unitID,    type: context.request.snapshot.type,  });  context.customData.creationPolicyChecked = true;  await next();});

submitChangeset s’exécute une fois par soumission logique. La concurrence sur les révisions peut relancer applyChangeset et commitChangeset avec un nouvel attempt. Ces Middleware doivent supporter les reprises et ne pas envoyer directement de messages externes irréversibles ni effectuer de facturation unique.

Portée de customData

customData est un objet modifiable par l’application, local à l’appel, adapté aux :

  • ID de locataire, ID de trace et loggers de requête ;
  • résultats ACL réutilisés dans l’appel ;
  • points de départ de chronométrage et étiquettes d’audit ;
  • objets métier partagés dans une chaîne Middleware.

Requêtes HTTP ordinaires, appels Service et Sessions WebSocket ont des cycles de vie différents. customData n’est ni persisté, ni envoyé au navigateur, ni journalisé automatiquement. Les données durables restent dans le stockage applicatif.

Modèle d’exécution des Events

Les Services inscrivent des écouteurs locaux au processus avec on(), qui renvoie un abonnement libérable :

TypeScript
const subscription = service.on("changesetCommitted", async (event) => {  metrics.increment("collaboration.changeset.committed", {    unitType: String(event.changeset.type),  });  localCache.delete(event.changeset.unitID);});subscription.dispose();

Les écouteurs d’un même Event s’exécutent séquentiellement dans l’ordre d’inscription et sont attendus. Leurs erreurs sont consignées et isolées du résultat métier déjà déterminé. L’échec d’un écouteur changesetCommitted n’annule donc pas les modifications déjà stockées par l’Adapter.

Les Events conviennent aux suites internes au processus, récupérables ou non critiques. Ils ne partagent pas la frontière transactionnelle du commit en base et ne garantissent pas la livraison à un système externe.

Events du Service central

EventFait confirméUsage courant
unitCreatedUnit et données initiales crééesCache de catalogue local ou index dérivé
changesetCommittedModifications confirmées et révision avancéeDiffusion, métriques, index dérivés
unitsDeletedLot de suppression confirmé par l’AdapterNettoyage de l’état associé dans le processus
unitsRecoveredLot de restauration confirmé par l’AdapterRestauration de l’état dérivé du processus

L’Endpoint écoute changesetCommitted sur le même Service et diffuse les changements confirmés aux clients inscrits dans l’Unit du processus courant, ou sur tous les nœuds lorsqu’un adaptateur pub/sub est configuré. Il expose aussi memberLeftUnit pour les départs explicites, déconnexions et libérations de l’Endpoint.

Points d’extension indépendants des modules

History, Thread Comment et Worktree n’héritent pas des Middleware ou Events du Collaboration Service central :

  • History fournit des Middleware de lecture pour getHistoryList, listHistoryCreators et getHistoryChangesets ;
  • Comment fournit ajout, réponse, édition, suppression, résolution et liste, plus commentCommitted ;
  • Worktree fournit cycle de vie et lecture/soumission/commit des brouillons, plus les Events de création, statut, fusion et commit de brouillon.

Installez les règles pour chaque module activé. Les Middleware de logs, locataire ou autorisation du cœur ne les couvrent pas automatiquement.

Poursuivez avec Identité et autorisations pour relier vos utilisateurs et ACL à ces frontières générales.

Que pensez-vous de ce guide ?

© 2026 DreamNum Co., Ltd.