Middlewares

Custom middleware

Like any other Effect, Marble.js defines a similar way for defining middlewares. In case of WebSocket protocol it is a function that transforms stream of incoming events 👉 stream of outgoing events.

Lets create a simple logger middleware like we previously did in case of HTTP.

logger.ws-middleware.ts
import { Event } from '@marblejs/core';
import { WsMiddlewareEffect } from '@marblejs/websockets';

export const logger$: WsMiddlewareEffect = event$ =>
  event$.pipe(
    tap(e => console.log(`type: ${e.type}, payload: ${e.payload}`)),
  );
  
// or an equivalent

export const logger$ = (event$: Observable<Event>): Observable<Event> =>
  event$.pipe(
    tap(e => console.log(`type: ${e.type}, payload: ${e.payload}`)),
  );

Similar to any other middleware or effect, we have to register it inside the listener.

webSocket.listener.ts
import { webSocketListener } from '@marblejs/websockets';
import { logger$ } from './logger.ws-middleware';

export const webSocketServer = webSocketListener({
  middlewares: [logger$],
  // ...
});

Middlewares composition

Similar to HTTP routes, you can compose middlewares in two ways:

  • globally (inside webSocketListener configuration object),

  • by composing it directly inside Effect event pipeline.

The use operator availabe in @marblejs/core package allows you to compose multiple types of middlewares - not only HTTP-related. Lets examine how can we compose the @marblejs/middleware-io middleware in event$ pipeline.

import { use } from '@marblejs/core';
import { WsEffect } from '@marblejs/websockets';
import { eventValidator$, t } from '@marblejs/middleware-io';

const User = t.type({
  name: t.string,
  age: t.number,
});

export const createUser$: WsEffect = event$ =>
  event$.pipe(
    matchEvent('CREATE_USER'),
    use(eventValidator$(User)),
    // ...   event.payload: { name: string, age: number }
  );

Last updated