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 metadataserver.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
-
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"] } };
-
Don’t store sensitive data: Metadata may be exposed to clients, so avoid passwords, tokens, or PII
-
Use merge strategies for partial updates:
"replace"
for complete resets"merge"
for updating top-level fields"deepMerge"
for updating nested objects
-
Implement cleanup patterns: Consider TTL for temporary metadata or cleanup jobs for stale data
-
Consider metadata as cache: Don’t rely on metadata for critical data that must persist - use your database as the source of truth