Skip to main content

Middleware

Introduction

Middleware methods have the access to ExecutionContext object. Which allows modifying the incoming and outgoing messages. The execution order is reversed for outgoing messages. Therefore, the first Middleware will have its onResponse() method executed as the last one.

Implementation

Creating of middleware is done by the implementation of the Middleware interface.

message-logger.middleware.ts
import { Middleware } from 'electron-broker';

export class MessageLoggerMiddleware implements Middleware {
public onRequest(context: ExecutionContext) {
const { eventId } = context.brokerEvent;

console.log(`[LOG] Received message with eventId: ${eventId}`)
}


public onResponse(context: ExecutionContext) {
const { eventId } = context.brokerEvent;

console.log(`[LOG] Sent message with eventId: ${eventId}`)
}
}

Applying middleware

Controller

Middleware can be applied globally on a controller, or its method by the use of UseMiddleware() decorator.

controller-middleware.ts
import MessageLoggerMiddleware from './message-logger.middleware.ts'
import RouteSpecificMiddleware from './route-specific.middleware.ts'

@Controller()
@UseMiddleware([new MessageLoggerMiddleware()])
export default class MiddlewareController {

@MessagePattern('test-route')
public testRoute(): string {
return "test";
}

@MessagePattern('another-route')
@UseMiddleware([new RouteSpecificMiddleware()])
public anotherRoute(): number {
return 1;
}
}

Controller-level middleware is always executed before method one. Therefore, the execution order in this example will start with MessageLoggerMiddleware first, then end with RouteSpecificMiddleware.

By use of the setMiddleware() method of Broker class, it's possible to apply middleware to each controller that you bind to Broker instance. But it has to be done before calling the start() method.

global-middleware.ts
import 'reflect-metadata';
import { Broker, BrokerClient, BrokerFactory } from 'electron-broker';
import MessageLogMiddleware from './message-logger.middleware.ts'

let broker: Broker;

async function createBroker() {
broker = await BrokerFactory.createProcessBroker();

broker.setMiddleware([new MessageLogMiddleware()])

broker.start();
}

createBroker();

Client

The client's middleware is set per BrokerClient class instance, with the use of the setMiddleware() method. And is executed after usage of send(), invoke(), and invokeRaw() methods.

client-middleware.ts
brokerClient.setMiddleware([new MessageLogMiddleware()])
info

Middleware set in Broker context, is not available in BrokerClient context.

Stopping execution

While creating the middleware feature there was no plan to disrupt the message handling. Therefore, the only way to stop it now is to throw an error either in onRequest() or onResponse() methods.

user-guard.middleware.ts
export default class UserGuardMiddleware { 
public onRequest(context: ExecutionContext) {
const { user } = context.brokerEvent;

if(!user) {
throw new Error('BrokerEvent lacks user property, stopping the execution...')
}
}
}

Lifecycle

Each time the middleware executes, the broker creates a copy of it and discards it after the message leaves its origin.