[]
        
(Showing Draft Content)

OT Middleware

The middleware mechanism in js-collaboration-ot allows developers to intercept and customize logic in the server-side OT (Operational Transformation) processing flow, such as permission validation or logging. This document explains how to use middleware, including its lifecycle, registration methods, and practical examples.

Basic Conception

Middleware is a core extension feature of DocumentServices, used to add custom logic during the processing stages of operations (op) and snapshots (snapshot).

Purpose:

  • Intercept and validate the operation flow

  • Modify the context or abort processing

Applicable Scenarios: User authentication, data validation, logging, performance monitoring, etc.

Difference from hooks: Middleware can interrupt the flow and return errors, whereas hooks only listen to events.

Lifecycle

Middleware covers multiple stages of the OT processing flow, with each stage corresponding to an action. Developers can selectively intervene in these stages. The following are the supported middleware actions and their trigger timings:

Action

Description

receive

Triggered when the server receives a client message, used for initial validation or logging.

submit

Triggered when an operation (op) is about to be submitted, allowing validation of operation data.

commit

Triggered when an operation is about to be committed to the database, after the operation has been transformed, allowing final validation.

afterWrite

Triggered after an operation is successfully written to the database, used for subsequent processing or notifications.

submitSnapshot

Triggered when a snapshot submission begins, allowing control over snapshot update logic.

apply

Triggered when an operation is about to be applied to a snapshot, with the snapshot still in its old state.

commitSnapshot

Triggered when a snapshot is about to be committed to the database.

readSnapshots

Triggered when one or more snapshots are loaded from the database (e.g., during fetch or subscribe), allowing rejection of specific snapshot reads.

readOp

Triggered when an operation is loaded from the database, allowing rejection of operation reads.

reply

Triggered when the document is about to send a non-error reply to the client.

Middleware Functions

Middleware is an asynchronous function that receives context and next parameters:

  • context: Contains contextual information for the current action (e.g., connection, request).

  • next: A function to continue execution, which can be called with an error to abort the flow.

    • await next(): Continues executing subsequent middleware or processing logic.

    • await next(error): Aborts the flow and returns an error.

Example: Basic Middleware

docService.use('submit', async (context, next) => {
    const user = context.connection.tags.get('user');
    if (user.role === 'editor') {
        console.log('Submitting operation:', context.request.op);
        await next(); // Continue
    } else {
        await next('No permission'); // Abort
    }
});

Registering Middleware

Middleware is registered using the use method of DocumentServices.

import { DocumentServices } from '@mescius/js-collaboration-ot';

const docService = new DocumentServices();

docService.use('receive', async (context, next) => {
    console.log('Message received:', context.request);
    await next();
});

Middleware Execution Order

Multiple middleware for the same action are executed in the order of registration. If a middleware calls next(error), subsequent middleware will be skipped.

Example: Execution Order

docService.use('submit', async (context, next) => {
  console.log('Middleware 1');
  await next();
});
docService.use('submit', async (context, next) => {
  console.log('Middleware 2');
  await next();
});
docService.use('submit', async (context, next) => {
  console.log('Middleware 3');
  await next();
});
// Output:
// Middleware 1
// Middleware 2
// Middleware 3

Example: Error Handling

docService.use('submit', async (context, next) => {
    console.log('Middleware 1');
    await next();
});
docService.use('submit', async (context, next) => {
    console.log('Middleware 2');
    await next('Test error');
});
docService.use('submit', async (context, next) => {
    console.log('Middleware 3'); // Will not be executed
    await next();
});
// Output:
// Middleware 1
// Middleware 2
// Error returned to the client, triggering the client-side error event

Use Cases

The following are common middleware use cases and their implementations:

1. Permission Validation

docService.use('submit', async (context, next) => {
    const user = context.connection.tags.get('user');
    if (user.role === 'editor') {
        console.log('Submitting operation:', context.request.op);
        await next(); // Continue
    } else {
        await next('No permission'); // Abort
    }
});

2. Logging

docService.use('afterWrite', async (context, next) => {
    console.log('Operation written to database:', context.request.op);
    await next();
});

Summary

Middleware provides powerful flow control capabilities for the server-side of js-collaboration-ot. By registering middleware at different lifecycle stages, you can implement validation, logging and other functionalities.