Client Metadata
Mesh provides two types of metadata storage: Connection Metadata and Room Metadata. Both are stored in Redis and accessible across all server instances. This page explains how to work with metadata from the client side.
Connection Metadata
Accessing Connection Metadata
Clients can retrieve connection metadata for any connection by providing the connection ID:
const = await .("conn123");
.(.userId, .username);
To fetch metadata for the current connection, call getConnectionMetadata
without any arguments:
const = await .();
.(.userId, .username);
Setting Connection Metadata
Clients can directly set metadata for their own connection using the built-in setConnectionMetadata
method:
// Replace entire metadata (default behavior)
await .({
: "online",
: { : "dark" }
});
Update Strategies
The setConnectionMetadata
method supports three different update strategies:
Replace Strategy (default):
// Replaces the entire metadata object
await .(
{ : "away" },
{ : "replace" }
);
Merge Strategy:
// Merges with existing metadata at the top level
await .(
{ : "away" },
{ : "merge" }
);
// Existing: { user: "john", preferences: { theme: "dark" } }
// Result: { user: "john", status: "away", preferences: { theme: "dark" } }
Deep Merge Strategy:
// Recursively merges nested objects
await .(
{ : { : false } },
{ : "deepMerge" }
);
// Existing: { user: "john", preferences: { theme: "dark", lang: "en" } }
// Result: { user: "john", preferences: { theme: "dark", lang: "en", notifications: false } }
Server-Controlled Metadata Pattern
For scenarios requiring server validation, you can still use custom commands:
// Server-side
.("update-user-status", async () => {
const { } = .;
// Server validation logic here
if (!["online", "away", "busy"].()) {
throw new ("Invalid status");
}
// Use merge strategy to preserve other metadata
await ..(.,
{ , : .() },
{ : "merge" }
);
return { : true };
});
// Client-side
await .("update-user-status", { : "away" });
Room Metadata
Accessing Room Metadata
You can fetch metadata for any room:
const = await .("lobby");
.(.topic, .maxUsers);
Setting Room Metadata
Room metadata can only be set server-side. Use custom commands to expose controlled updates.
The server-side room metadata API supports the same strategies as connection metadata:
// Server-side
.("update-room-topic", async () => {
const { , } = .;
// Validate permissions here
if (!await isRoomModerator(., )) {
throw new ("Insufficient permissions");
}
await ..(, {
,
: .()
}, { : "merge" });
return { : true };
});
// Client-side
await .("update-room-topic", {
: "lobby",
: "Welcome to the lobby!"
});
Metadata with Presence
Metadata is especially powerful when combined with presence updates. Here’s how to show user information alongside presence events:
const { , } = await .(
"lobby",
async () => {
const = await .(.);
if (. === "join") {
.(`${.username} joined the room`);
addUserToUI();
} else if (. === "leave") {
.(`${.username} left the room`);
removeUserFromUI(.userId);
}
}
);
// Load initial user list with metadata
if () {
const = await .(
.(() => .())
);
.(addUserToUI);
}
Metadata Change Notifications
Mesh doesn’t automatically push metadata updates to clients. If you need live metadata updates, implement them using channels or records:
Using Channels
// Server-side: Notify when user metadata changes
.("update-user-metadata", async () => {
const { } = .;
// Update the metadata
await ..(., , { : "merge" });
// Broadcast the change
const = await ..(..);
for (const of ) {
await .(
`room:${}:metadata-updates`,
.({
: ..,
: await ..(.)
})
);
}
return { : true };
});
// Client-side: Listen for metadata changes
await .("room:lobby:metadata-updates", () => {
const { , } = .();
updateUserInUI(, );
});
Using Records
// Server-side: Store user metadata as a record
.("update-user-metadata", async () => {
const { } = .;
// Update both connection metadata and a record
await ..(., , { : "merge" });
await .(
`user:${..}:metadata`,
,
{ : "merge" }
);
return { : true };
});
// Client-side: Subscribe to metadata record
await .("user:conn123:metadata", () => {
updateUserInUI("conn123", .);
});
Best Practices
-
Use descriptive metadata structure:
const metadata = { user: { id: "123", username: "john", avatar: "/avatars/john.png" }, session: { status: "online", lastSeen: Date.now() }, preferences: { theme: "dark", lang: "en" } };
-
Leverage merge strategies for partial updates:
// Client can safely update status without affecting user info await client.setConnectionMetadata( { session: { status: "away" } }, { strategy: "deepMerge" } );
-
Keep metadata compact and serializable - Avoid large objects or functions
-
Use metadata for UI state, not application logic - Critical data should be stored in your database
-
Consider metadata lifecycle - Connection metadata is automatically cleaned up when connections close