Middleware

Composable message processing pipeline. Chain functions that run on inbound messages before your handler logic. Each middleware receives a context and a next function - call next() to continue, skip it to stop the chain.

Setup

// src/lib/server/pipeline.js
import { createMiddleware } from 'svelte-adapter-uws/plugins/middleware';

export const pipeline = createMiddleware(
  // logging
  async (ctx, next) => {
    console.log(`[${ctx.topic}] ${ctx.event}`);
    await next();
  },
  // auth check
  async (ctx, next) => {
    const userId = ctx.ws.getUserData()?.userId;
    if (!userId) return; // stop chain -- unauthenticated
    ctx.locals.userId = userId;
    await next();
  },
  // data enrichment
  async (ctx, next) => {
    ctx.data = { ...ctx.data, processedAt: Date.now() };
    await next();
  }
);

Usage

// src/hooks.ws.js
import { pipeline } from '$lib/server/pipeline';

export async function message(ws, { data, platform }) {
  const msg = JSON.parse(Buffer.from(data).toString());
  const ctx = await pipeline.run(ws, msg, platform);
  if (!ctx) return; // chain was stopped (e.g. auth failed)

  // ctx.locals.userId is available here
  // ctx.data has the enriched data
}

API

MethodDescription
pipeline.run(ws, message, platform)Execute the chain. Returns context or null if stopped
pipeline.use(fn)Append a middleware at runtime

The context object passed to each middleware:

FieldDescription
ctx.wsThe WebSocket connection
ctx.messageOriginal parsed message
ctx.topicMessage topic (mutable)
ctx.eventMessage event (mutable)
ctx.dataMessage data (mutable)
ctx.platformPlatform reference
ctx.localsScratch space for middleware to share data

Limitations

  • Server-side only. No client component.
  • No state. The middleware itself is stateless - it’s a pure pipeline. Use ctx.locals to pass data between middlewares within a single message.
  • Double next() guard. Calling next() twice in the same middleware is a no-op (the second call does nothing).

Was this page helpful?