Skip to Content
Server SDKMiddleware

Server: Middleware

Middleware functions run before command handlers. Use them for auth, validation, logging, or injecting context.

Global middleware

Run before every command:

.(async () => { const = await ..(.); if (!?.userId) throw new ("Unauthorized"); .user = { : .userId, : .role, }; });

Per-command middleware

Attach middleware to individual commands. These run after global middleware:

const = async () => { const { } = .payload; if (!?.includes("@")) throw new ("Invalid email"); }; .( "register", async () => { // ctx.payload is already validated return { : true }; }, [] );

Middleware can be useful for things like throttling client messages:

const = new (); .(async () => { if (. !== "cursor-update") return; const = .(); const = .(..) || 0; if ( - < 50) { throw new ("Too fast"); // or just return } .(.., ); });

Middleware order

  1. Global middleware (in the order registered)
  2. Per-command middleware
  3. Command handler

If any middleware throws, the error is sent back to the client.

Modifying context

You can attach custom fields to ctx for downstream use:

.(async () => { const = await ..(.); .user = { : ?.userId, : await fetchPermissions(?.userId), }; }); .("admin:action", async () => { if (!.user?.permissions.includes("admin")) { throw new ("Admin required"); } return { : true }; });

Client error response

If a middleware throws, the client receives an error:

{ error: "Admin required", code: "ESERVER", name: "Error" }

Use cases

These are mostly pseudocode, but hopefully help illustrate what this is useful for:

Auth

.(async () => { const = await ..(.); if (!?.userId) throw new ("Login required"); });

Validation

const = () => { if (!.payload?.title) throw new ("Missing title"); }; .("post:create", handler, []);

Logging

.(() => { .(`[cmd] ${.} from ${..}`); });

Rate limiting

const = createRateLimiter(10, 60_000); // 10 req/min .(() => { if (!.allow(..)) throw new ("Rate limit exceeded"); });
Last updated on
© 2025