Skip to Content
Server SDKCommands

Server: Commands

Expose structured request/response handlers that clients can call over WebSocket.

Basic usage

server.exposeCommand("echo", async (ctx) => { return `echo: ${ctx.payload}`; });

Clients can call this command with:

const response = await client.command("echo", "Hello!"); console.log(response); // "echo: Hello!"

Command handlers receive an object of type MeshContext with the following properties:

  • ctx.command — command name
  • ctx.payload — data sent by the client
  • ctx.connection — the WebSocket connection
  • Additional fields added by middleware

You can return any JSON-serializable value.

server.exposeCommand("add", (ctx) => { return ctx.payload.a + ctx.payload.b; });

Errors

Throwing inside a command sends the error back to the client in a predictable format.

server.exposeCommand("divide", (ctx) => { const { a, b } = ctx.payload; if (b === 0) throw new Error("Cannot divide by zero"); return a / b; });

The client receives:

{ error: "Cannot divide by zero", code: "ESERVER", name: "Error" }

To customize the error code and name:

import { CodeError } from "@mesh-kit/core/client"; server.exposeCommand("login", () => { throw new CodeError("Invalid credentials", "EAUTH", "AuthError"); });

Client receives:

{ error: "Invalid credentials", code: "EAUTH", name: "AuthError" }

Or if you prefer, you can return an object with error, code and name properties instead of throwing anything. It’s recommended to stick to this error format for the sake of consistency.

Middleware

Attach middleware to validate input, enforce auth, or inject data:

const validate = (ctx) => { if (!ctx.payload.email.includes("@")) throw new Error("Invalid email"); }; server.exposeCommand("update-profile", async (ctx) => { return { success: true }; }, [validate]);

See middleware for more details.

Built-in commands

Mesh includes built-in commands for rooms, presence, channels, metadata, and records:

  • "mesh/join-room", "mesh/leave-room"
  • "mesh/subscribe-presence", "mesh/unsubscribe-presence"
  • "mesh/publish-presence-state", "mesh/clear-presence-state"
  • "mesh/subscribe-channel", "mesh/unsubscribe-channel"
  • "mesh/get-connection-metadata", "mesh/get-my-connection-metadata"
  • "mesh/get-room-metadata"
  • "mesh/subscribe-record", "mesh/unsubscribe-record"
  • "mesh/publish-record-update"

These power the SDK’s features and can be intercepted with middleware if needed.

For example, you could:

  • Prevent access to certain rooms based on user roles
  • Validate presence state schema before publishing
  • Enforce metadata structure for all connections
  • Restrict record updates to certain users
  • Control access to channels based on user permissions
  • Validate record update schema with middleware

Tips

  • Use namespaced commands like "chat:send" or "user:create"
  • Validate inputs with middleware
  • Keep handlers simple and focused
  • Return only what the client needs
Last updated on
© 2025