Skip to Content
Server SDKMetadata

Server: Metadata

Mesh supports two types of metadata:

  • Connection metadata: data attached to individual WebSocket connections
  • Room metadata: data associated with named rooms

All metadata is stored in Redis and accessible across all server instances.

Connection metadata

Useful for identifying users, storing roles, or tracking per-connection session info.

Set metadata

await ..(connection, { : "user123", : "admin", : .(), });

Update Strategies

Connection metadata supports the same update strategies as client metadata:

// Replace entire metadata (default) await ..(connection, newMetadata); // Merge with existing metadata await ..(connection, partialMetadata, { : "merge" }); // Deep merge nested objects await ..(connection, nestedUpdate, { : "deepMerge" });

Get metadata

Single connection

const = await ..(connection);

The getMetadata method requires a Connection object, not a connection ID string. To get metadata by connection ID, first retrieve the connection object using server.connectionManager.getLocalConnection(connectionId).

All connections

Returns an array of connection objects with id and metadata:

const = await ..(); // [ // { id: "conn1", metadata: { userId: "user123", role: "admin" } }, // { id: "conn2", metadata: { userId: "user456", role: "user" } }, // ... // ]

All connections in a room

Returns an array of connection objects with id and metadata:

const = await ..("lobby"); // [ // { id: "conn1", metadata: { userId: "user123", username: "Alice" } }, // { id: "conn2", metadata: { userId: "user456", username: "Bob" } }, // ]

Update metadata

To update existing metadata while preserving other fields, use the merge strategies:

// Merge approach - preserves existing fields await ..(connection, { : .(), }, { : "merge" }); // Manual merge approach (if you need custom logic) const = await ..(connection); await ..(connection, { ..., : .(), });

Cleanup

Connection metadata is automatically removed when the connection disconnects. This happens during the connection cleanup process in server.cleanupConnection().

Room metadata

Useful for storing room-level settings, topic info, or ownership metadata.

Set metadata

await ..("lobby", { : "General Discussion", : "user123", : 10, : .(), });

This completely replaces any existing metadata for the room.

Update Strategies

Room metadata supports the same update strategies as connection metadata:

// Replace entire metadata (default) await ..("lobby", newMetadata, { : "replace" }); // Merge with existing metadata await ..("lobby", partialMetadata, { : "merge" }); // Deep merge nested objects await ..("lobby", nestedUpdate, { : "deepMerge" });

Get metadata

Single room

const = await ..("lobby"); // { topic: "General Discussion", createdBy: "user123", maxUsers: 10 }

All rooms

Returns an array of room objects with id and metadata:

const = await ..(); // [ // { id: "lobby", metadata: { topic: "General Discussion", createdBy: "user123" } }, // { id: "dev", metadata: { topic: "Development Chat", createdBy: "user456" } } // ]

Delete metadata

To completely remove room metadata, you have two options:

Option 1: Delete the room entirely (recommended)

await .(roomName);

This removes all occupants AND deletes the room metadata permanently.

Option 2: Manual metadata deletion

await ..redis.del(`mesh:roommeta:${roomName}`);

This only deletes the metadata but leaves the room structure intact.

Room cleanup methods:

  • server.clearRoom(roomName) - Removes all occupants but preserves room metadata
  • server.deleteRoom(roomName) - Removes all occupants and deletes room metadata permanently
⚠️

Setting metadata to null with setMetadata(roomName, null) stores the JSON string "null" rather than deleting the metadata. Use deleteRoom() for complete room deletion.

Exposing metadata to clients

Connection metadata

Clients can access their own metadata, or the metadata of other connections:

// get metadata for some connection by ID const = await .("conn123"); // get my metadata const = await .();

Room metadata

// get room metadata const = await .("lobby");

Room metadata cannot be set directly by clients. Use custom commands for controlled updates:

// server .("update-room-topic", async () => { const { , } = .; // validation logic if (!await isRoomModerator(., )) { throw new ("Insufficient permissions"); } await ..(, { , : .(), : .. }, { : "merge" }); return { : true }; }); // client await .("update-room-topic", { : "lobby", : "New Topic" });

Useful patterns

Connection metadata with authentication

// Set initial metadata during connection .(async () => { // Extract user info from connection request const = await authenticateConnection(); await ..(, { , : .(), : await getUserPermissions() }); }); // Update metadata during session .("update-user-status", async () => { const { } = .; await ..(., { , : .() }, { : "merge" }); return { : true }; });

Room metadata with lifecycle management

// Create room with metadata .("create-room", async () => { const { , , } = .; await ..(, { : , , : .., : .(), : 0 }); return { : true }; }); // Update member count when users join/leave .(async () => { .("mesh/join-room", async () => { const = await ..(); if () { await ..(, { : (.memberCount || 0) + 1 }, { : "merge" }); } }); // similarly handle "mesh/leave-room" });

Metadata-based filtering

// Get all public rooms with space available const = await ..(); const = .( => ..type === "public" && ..memberCount < ..maxUsers ); // Get all rooms created by a specific user const = .( => ..createdBy === "user123" ); // Complex filtering with multiple criteria const = .( => ..type === "public") .( => ..premium === true) .((, ) => ..memberCount - ..memberCount); // Connection filtering examples const = await ..(); const = .( => ..role === "admin") .( => ..status === "online");

Best practices

  1. Use consistent data structures:

    const connectionMetadata = { user: { id: "123", username: "alice" }, session: { status: "online", joinedAt: Date.now() }, permissions: ["read", "write"] }; const roomMetadata = { config: { type: "public", maxUsers: 50 }, stats: { memberCount: 25, messagesCount: 1532 }, moderation: { createdBy: "user123", moderators: ["user456"] } };
  2. Don’t store sensitive data: Metadata may be exposed to clients, so avoid passwords, tokens, or PII

  3. Use merge strategies for partial updates:

  • "replace" for complete resets
  • "merge" for updating top-level fields
  • "deepMerge" for updating nested objects
  1. Implement cleanup patterns: Consider TTL for temporary metadata or cleanup jobs for stale data

  2. Consider metadata as cache: Don’t rely on metadata for critical data that must persist - use your database as the source of truth

Last updated on
© 2025