Build real-time messaging applications using Azure Web PubSub SDKs for JavaScript (@azure/web-pubsub, @azure/web-pubsub-client). Use when implementing WebSocket-based real-time features, pub/sub messaging, group chat, or live notifications.
Add this skill
npx mdskills install sickn33/azure-web-pubsub-tsComprehensive Azure Web PubSub reference with clear server/client examples and authentication patterns
1---2name: azure-web-pubsub-ts3description: Build real-time messaging applications using Azure Web PubSub SDKs for JavaScript (@azure/web-pubsub, @azure/web-pubsub-client). Use when implementing WebSocket-based real-time features, pub/sub messaging, group chat, or live notifications.4package: "@azure/web-pubsub, @azure/web-pubsub-client"5---67# Azure Web PubSub SDKs for TypeScript89Real-time messaging with WebSocket connections and pub/sub patterns.1011## Installation1213```bash14# Server-side management15npm install @azure/web-pubsub @azure/identity1617# Client-side real-time messaging18npm install @azure/web-pubsub-client1920# Express middleware for event handlers21npm install @azure/web-pubsub-express22```2324## Environment Variables2526```bash27WEBPUBSUB_CONNECTION_STRING=Endpoint=https://<resource>.webpubsub.azure.com;AccessKey=<key>;Version=1.0;28WEBPUBSUB_ENDPOINT=https://<resource>.webpubsub.azure.com29```3031## Server-Side: WebPubSubServiceClient3233### Authentication3435```typescript36import { WebPubSubServiceClient, AzureKeyCredential } from "@azure/web-pubsub";37import { DefaultAzureCredential } from "@azure/identity";3839// Connection string40const client = new WebPubSubServiceClient(41 process.env.WEBPUBSUB_CONNECTION_STRING!,42 "chat" // hub name43);4445// DefaultAzureCredential (recommended)46const client2 = new WebPubSubServiceClient(47 process.env.WEBPUBSUB_ENDPOINT!,48 new DefaultAzureCredential(),49 "chat"50);5152// AzureKeyCredential53const client3 = new WebPubSubServiceClient(54 process.env.WEBPUBSUB_ENDPOINT!,55 new AzureKeyCredential("<access-key>"),56 "chat"57);58```5960### Generate Client Access Token6162```typescript63// Basic token64const token = await client.getClientAccessToken();65console.log(token.url); // wss://...?access_token=...6667// Token with user ID68const userToken = await client.getClientAccessToken({69 userId: "user123",70});7172// Token with permissions73const permToken = await client.getClientAccessToken({74 userId: "user123",75 roles: [76 "webpubsub.joinLeaveGroup",77 "webpubsub.sendToGroup",78 "webpubsub.sendToGroup.chat-room", // specific group79 ],80 groups: ["chat-room"], // auto-join on connect81 expirationTimeInMinutes: 60,82});83```8485### Send Messages8687```typescript88// Broadcast to all connections in hub89await client.sendToAll({ message: "Hello everyone!" });90await client.sendToAll("Plain text", { contentType: "text/plain" });9192// Send to specific user (all their connections)93await client.sendToUser("user123", { message: "Hello!" });9495// Send to specific connection96await client.sendToConnection("connectionId", { data: "Direct message" });9798// Send with filter (OData syntax)99await client.sendToAll({ message: "Filtered" }, {100 filter: "userId ne 'admin'",101});102```103104### Group Management105106```typescript107const group = client.group("chat-room");108109// Add user/connection to group110await group.addUser("user123");111await group.addConnection("connectionId");112113// Remove from group114await group.removeUser("user123");115116// Send to group117await group.sendToAll({ message: "Group message" });118119// Close all connections in group120await group.closeAllConnections({ reason: "Maintenance" });121```122123### Connection Management124125```typescript126// Check existence127const userExists = await client.userExists("user123");128const connExists = await client.connectionExists("connectionId");129130// Close connections131await client.closeConnection("connectionId", { reason: "Kicked" });132await client.closeUserConnections("user123");133await client.closeAllConnections();134135// Permissions136await client.grantPermission("connectionId", "sendToGroup", { targetName: "chat" });137await client.revokePermission("connectionId", "sendToGroup", { targetName: "chat" });138```139140## Client-Side: WebPubSubClient141142### Connect143144```typescript145import { WebPubSubClient } from "@azure/web-pubsub-client";146147// Direct URL148const client = new WebPubSubClient("<client-access-url>");149150// Dynamic URL from negotiate endpoint151const client2 = new WebPubSubClient({152 getClientAccessUrl: async () => {153 const response = await fetch("/negotiate");154 const { url } = await response.json();155 return url;156 },157});158159// Register handlers BEFORE starting160client.on("connected", (e) => {161 console.log(`Connected: ${e.connectionId}`);162});163164client.on("group-message", (e) => {165 console.log(`${e.message.group}: ${e.message.data}`);166});167168await client.start();169```170171### Send Messages172173```typescript174// Join group first175await client.joinGroup("chat-room");176177// Send to group178await client.sendToGroup("chat-room", "Hello!", "text");179await client.sendToGroup("chat-room", { type: "message", content: "Hi" }, "json");180181// Send options182await client.sendToGroup("chat-room", "Hello", "text", {183 noEcho: true, // Don't echo back to sender184 fireAndForget: true, // Don't wait for ack185});186187// Send event to server188await client.sendEvent("userAction", { action: "typing" }, "json");189```190191### Event Handlers192193```typescript194// Connection lifecycle195client.on("connected", (e) => {196 console.log(`Connected: ${e.connectionId}, User: ${e.userId}`);197});198199client.on("disconnected", (e) => {200 console.log(`Disconnected: ${e.message}`);201});202203client.on("stopped", () => {204 console.log("Client stopped");205});206207// Messages208client.on("group-message", (e) => {209 console.log(`[${e.message.group}] ${e.message.fromUserId}: ${e.message.data}`);210});211212client.on("server-message", (e) => {213 console.log(`Server: ${e.message.data}`);214});215216// Rejoin failure217client.on("rejoin-group-failed", (e) => {218 console.log(`Failed to rejoin ${e.group}: ${e.error}`);219});220```221222## Express Event Handler223224```typescript225import express from "express";226import { WebPubSubEventHandler } from "@azure/web-pubsub-express";227228const app = express();229230const handler = new WebPubSubEventHandler("chat", {231 path: "/api/webpubsub/hubs/chat/",232233 // Blocking: approve/reject connection234 handleConnect: (req, res) => {235 if (!req.claims?.sub) {236 res.fail(401, "Authentication required");237 return;238 }239 res.success({240 userId: req.claims.sub[0],241 groups: ["general"],242 roles: ["webpubsub.sendToGroup"],243 });244 },245246 // Blocking: handle custom events247 handleUserEvent: (req, res) => {248 console.log(`Event from ${req.context.userId}:`, req.data);249 res.success(`Received: ${req.data}`, "text");250 },251252 // Non-blocking253 onConnected: (req) => {254 console.log(`Client connected: ${req.context.connectionId}`);255 },256257 onDisconnected: (req) => {258 console.log(`Client disconnected: ${req.context.connectionId}`);259 },260});261262app.use(handler.getMiddleware());263264// Negotiate endpoint265app.get("/negotiate", async (req, res) => {266 const token = await serviceClient.getClientAccessToken({267 userId: req.user?.id,268 });269 res.json({ url: token.url });270});271272app.listen(8080);273```274275## Key Types276277```typescript278// Server279import {280 WebPubSubServiceClient,281 WebPubSubGroup,282 GenerateClientTokenOptions,283 HubSendToAllOptions,284} from "@azure/web-pubsub";285286// Client287import {288 WebPubSubClient,289 WebPubSubClientOptions,290 OnConnectedArgs,291 OnGroupDataMessageArgs,292} from "@azure/web-pubsub-client";293294// Express295import {296 WebPubSubEventHandler,297 ConnectRequest,298 UserEventRequest,299 ConnectResponseHandler,300} from "@azure/web-pubsub-express";301```302303## Best Practices3043051. **Use Entra ID auth** - `DefaultAzureCredential` for production3062. **Register handlers before start** - Don't miss initial events3073. **Use groups for channels** - Organize messages by topic/room3084. **Handle reconnection** - Client auto-reconnects by default3095. **Validate in handleConnect** - Reject unauthorized connections early3106. **Use noEcho** - Prevent message echo back to sender when needed311
Full transparency — inspect the skill content before installing.