skills/sickn33/azure-web-pubsub-ts/SKILL.md
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.
npx skillsauth add aiskillstore/marketplace azure-web-pubsub-tsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Real-time messaging with WebSocket connections and pub/sub patterns.
# Server-side management
npm install @azure/web-pubsub @azure/identity
# Client-side real-time messaging
npm install @azure/web-pubsub-client
# Express middleware for event handlers
npm install @azure/web-pubsub-express
WEBPUBSUB_CONNECTION_STRING=Endpoint=https://<resource>.webpubsub.azure.com;AccessKey=<key>;Version=1.0;
WEBPUBSUB_ENDPOINT=https://<resource>.webpubsub.azure.com
import { WebPubSubServiceClient, AzureKeyCredential } from "@azure/web-pubsub";
import { DefaultAzureCredential } from "@azure/identity";
// Connection string
const client = new WebPubSubServiceClient(
process.env.WEBPUBSUB_CONNECTION_STRING!,
"chat" // hub name
);
// DefaultAzureCredential (recommended)
const client2 = new WebPubSubServiceClient(
process.env.WEBPUBSUB_ENDPOINT!,
new DefaultAzureCredential(),
"chat"
);
// AzureKeyCredential
const client3 = new WebPubSubServiceClient(
process.env.WEBPUBSUB_ENDPOINT!,
new AzureKeyCredential("<access-key>"),
"chat"
);
// Basic token
const token = await client.getClientAccessToken();
console.log(token.url); // wss://...?access_token=...
// Token with user ID
const userToken = await client.getClientAccessToken({
userId: "user123",
});
// Token with permissions
const permToken = await client.getClientAccessToken({
userId: "user123",
roles: [
"webpubsub.joinLeaveGroup",
"webpubsub.sendToGroup",
"webpubsub.sendToGroup.chat-room", // specific group
],
groups: ["chat-room"], // auto-join on connect
expirationTimeInMinutes: 60,
});
// Broadcast to all connections in hub
await client.sendToAll({ message: "Hello everyone!" });
await client.sendToAll("Plain text", { contentType: "text/plain" });
// Send to specific user (all their connections)
await client.sendToUser("user123", { message: "Hello!" });
// Send to specific connection
await client.sendToConnection("connectionId", { data: "Direct message" });
// Send with filter (OData syntax)
await client.sendToAll({ message: "Filtered" }, {
filter: "userId ne 'admin'",
});
const group = client.group("chat-room");
// Add user/connection to group
await group.addUser("user123");
await group.addConnection("connectionId");
// Remove from group
await group.removeUser("user123");
// Send to group
await group.sendToAll({ message: "Group message" });
// Close all connections in group
await group.closeAllConnections({ reason: "Maintenance" });
// Check existence
const userExists = await client.userExists("user123");
const connExists = await client.connectionExists("connectionId");
// Close connections
await client.closeConnection("connectionId", { reason: "Kicked" });
await client.closeUserConnections("user123");
await client.closeAllConnections();
// Permissions
await client.grantPermission("connectionId", "sendToGroup", { targetName: "chat" });
await client.revokePermission("connectionId", "sendToGroup", { targetName: "chat" });
import { WebPubSubClient } from "@azure/web-pubsub-client";
// Direct URL
const client = new WebPubSubClient("<client-access-url>");
// Dynamic URL from negotiate endpoint
const client2 = new WebPubSubClient({
getClientAccessUrl: async () => {
const response = await fetch("/negotiate");
const { url } = await response.json();
return url;
},
});
// Register handlers BEFORE starting
client.on("connected", (e) => {
console.log(`Connected: ${e.connectionId}`);
});
client.on("group-message", (e) => {
console.log(`${e.message.group}: ${e.message.data}`);
});
await client.start();
// Join group first
await client.joinGroup("chat-room");
// Send to group
await client.sendToGroup("chat-room", "Hello!", "text");
await client.sendToGroup("chat-room", { type: "message", content: "Hi" }, "json");
// Send options
await client.sendToGroup("chat-room", "Hello", "text", {
noEcho: true, // Don't echo back to sender
fireAndForget: true, // Don't wait for ack
});
// Send event to server
await client.sendEvent("userAction", { action: "typing" }, "json");
// Connection lifecycle
client.on("connected", (e) => {
console.log(`Connected: ${e.connectionId}, User: ${e.userId}`);
});
client.on("disconnected", (e) => {
console.log(`Disconnected: ${e.message}`);
});
client.on("stopped", () => {
console.log("Client stopped");
});
// Messages
client.on("group-message", (e) => {
console.log(`[${e.message.group}] ${e.message.fromUserId}: ${e.message.data}`);
});
client.on("server-message", (e) => {
console.log(`Server: ${e.message.data}`);
});
// Rejoin failure
client.on("rejoin-group-failed", (e) => {
console.log(`Failed to rejoin ${e.group}: ${e.error}`);
});
import express from "express";
import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
const app = express();
const handler = new WebPubSubEventHandler("chat", {
path: "/api/webpubsub/hubs/chat/",
// Blocking: approve/reject connection
handleConnect: (req, res) => {
if (!req.claims?.sub) {
res.fail(401, "Authentication required");
return;
}
res.success({
userId: req.claims.sub[0],
groups: ["general"],
roles: ["webpubsub.sendToGroup"],
});
},
// Blocking: handle custom events
handleUserEvent: (req, res) => {
console.log(`Event from ${req.context.userId}:`, req.data);
res.success(`Received: ${req.data}`, "text");
},
// Non-blocking
onConnected: (req) => {
console.log(`Client connected: ${req.context.connectionId}`);
},
onDisconnected: (req) => {
console.log(`Client disconnected: ${req.context.connectionId}`);
},
});
app.use(handler.getMiddleware());
// Negotiate endpoint
app.get("/negotiate", async (req, res) => {
const token = await serviceClient.getClientAccessToken({
userId: req.user?.id,
});
res.json({ url: token.url });
});
app.listen(8080);
// Server
import {
WebPubSubServiceClient,
WebPubSubGroup,
GenerateClientTokenOptions,
HubSendToAllOptions,
} from "@azure/web-pubsub";
// Client
import {
WebPubSubClient,
WebPubSubClientOptions,
OnConnectedArgs,
OnGroupDataMessageArgs,
} from "@azure/web-pubsub-client";
// Express
import {
WebPubSubEventHandler,
ConnectRequest,
UserEventRequest,
ConnectResponseHandler,
} from "@azure/web-pubsub-express";
DefaultAzureCredential for productiondevelopment
Apple Human Interface Guidelines for content display components. Use this skill when the user asks about charts component, collection view, image view, web view, color well, image well, activity view, lockup, data visualization, content display, displaying images, rendering web content, color pickers, or presenting collections of items in Apple apps. Also use when the user says how should I display charts, what's the best way to show images, should I use a web view, how do I build a grid of items, what component shows media, or how do I present a share sheet. Cross-references: hig-foundations for color/typography/accessibility, hig-patterns for data visualization patterns, hig-components-layout for structural containers, hig-platforms for platform-specific component behavior.
tools
Automate HelpDesk tasks via Rube MCP (Composio): list tickets, manage views, use canned responses, and configure custom fields. Always search tools first for current schemas.
testing
Expert Haskell engineer specializing in advanced type systems, pure functional design, and high-reliability software. Use PROACTIVELY for type-level programming, concurrency, and architecture guidance.
tools
GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.