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.
Accessing Connection Metadata
Clients can retrieve connection metadata directly:
const metadata = await client.getConnectionMetadata("conn123");
console.log(metadata.userId, metadata.username);
To fetch metadata for the current connection, use getConnectionMetadata
without any arguments:
const metadata = await client.getConnectionMetadata();
console.log(metadata.userId, metadata.username);
Updating Connection Metadata
There’s no built-in way for clients to update metadata directly (for safety), but you can expose a custom command on the server:
// Server-side
server.exposeCommand("update-my-metadata", async (ctx) => {
const { status } = ctx.payload;
const existing = await server.connectionManager.getMetadata(ctx.connection);
await server.connectionManager.setMetadata(ctx.connection, {
...existing,
status,
});
return true;
});
// Client-side
await client.command("update-my-metadata", {
status: "away",
});
Accessing Room Metadata
You can also fetch metadata for a specific room:
const meta = await client.getRoomMetadata("lobby");
console.log(meta.topic, meta.maxUsers);
Updating Room Metadata
Room metadata updates must also go through a server-exposed command:
// Server-side
server.exposeCommand("update-room-metadata", async (ctx) => {
const { roomName, metadata } = ctx.payload;
await server.roomManager.updateMetadata(roomName, metadata);
return true;
});
// Client-side
await client.command("update-room-metadata", {
roomName: "lobby",
metadata: { topic: "New Topic" },
});
Combining Metadata with Presence
Metadata is especially useful alongside presence updates — for example, to show usernames and avatars next to connection events:
const { success, present } = await client.subscribePresence(
"lobby",
async (update) => {
const metadata = await client.getConnectionMetadata(update.connectionId);
if (update.type === "join") {
addUserToUI(metadata);
} else if (update.type === "leave") {
removeUserFromUI(metadata.userId);
}
}
);
if (success) {
const allMetadata = await Promise.all(
present.map((connId) => client.getConnectionMetadata(connId))
);
allMetadata.forEach(addUserToUI);
}
Subscribing to Metadata Changes
Mesh doesn’t currently push metadata updates automatically. You can simulate live updates using either channels or records.
Using Channels
// Server-side
await server.publishToChannel(
`user:${userId}:metadata`,
JSON.stringify(updatedMetadata)
);
// Client-side
await client.subscribeChannel(`user:${userId}:metadata`, (message) => {
const meta = JSON.parse(message);
updateUserInUI(meta);
});
Using Records
// Server-side
await server.publishRecordUpdate(`user:${userId}:metadata`, updatedMetadata);
// Client-side
await client.subscribeRecord(`user:${userId}:metadata`, (update) => {
updateUserInUI(update.full);
});
Best Practices
- Use the built-in
getConnectionMetadata
andgetRoomMetadata
when possible - Expose specific server commands for controlled metadata updates
- Do not store sensitive data in metadata
- Keep metadata compact and fast to serialize
- Use records or channels to simulate metadata subscriptions when needed