azure-web-pubsub-ts – DevOps & Infrastructure skill for AI agents
Real-time messaging with WebSocket connections and pub/sub patterns.
Install the azure-web-pubsub-ts skill with one command
Pick the tool you use, copy the command and paste it in your terminal. The skill lands in that tool's skills folder and is ready in your next session.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/azure-web-pubsub-ts-b2600.tgz | tar xz -C ~/.claude/skills
Call it in Claude Code: /azure-web-pubsub-ts – then describe the task you want done.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/azure-web-pubsub-ts-b2600.tgz | tar xz -C ~/.codex/skills
Call it in Codex: $azure-web-pubsub-ts – then describe the task you want done.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/azure-web-pubsub-ts-b2600.tgz | tar xz -C ~/.hermes/skills
Call it in Hermes Agent: /azure-web-pubsub-ts – then describe the task you want done.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/azure-web-pubsub-ts-b2600.tgz | tar xz -C ~/.deepcode/skills
Call it in DeepSeek: /azure-web-pubsub-ts – then describe the task you want done.
SKILL.md content
Azure Web PubSub SDKs for TypeScript
Real-time messaging with WebSocket connections and pub/sub patterns.
Installation
# 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-expressEnvironment Variables
WEBPUBSUB_CONNECTION_STRING=Endpoint=https://<resource>.webpubsub.azure.com;AccessKey=<key>;Version=1.0;
WEBPUBSUB_ENDPOINT=https://<resource>.webpubsub.azure.comServer-Side: WebPubSubServiceClient
Authentication
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"
);Generate Client Access Token
// Basic token
const token = await client.getClientAccessToken();
// token.url contains a bearer credential: return only to the authorized client, never log it.
// Token with user ID
const userToken = await client.getClientAccessToken({
userId: "user123",
});
// Token with permissions
const permToken = await client.getClientAccessToken({
userId: "user123",
roles: [
"webpubsub.joinLeaveGroup.chat-room",
"webpubsub.sendToGroup.chat-room", // specific group
],
groups: ["chat-room"], // auto-join on connect
expirationTimeInMinutes: 60,
});Send Messages
// 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'",
});Group Management
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" });Connection Management
// 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" });Client-Side: WebPubSubClient
Connect
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();Send Messages
// 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");Event Handlers
// 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}`);
});Express Event Handler
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.general"],
});
},
// 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) => {
if (!req.user?.id) return res.status(401).end();
const token = await serviceClient.getClientAccessToken({
userId: req.user.id, // prerequisite: verified authenticated application session
});
res.json({ url: token.url });
});
app.listen(8080);Key Types
// 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";Best Practices
1. Use Entra ID auth - DefaultAzureCredential for production
2. Register handlers before start - Don't miss initial events
3. Use groups for channels - Organize messages by topic/room
4. Handle reconnection - Client auto-reconnects by default
5. Validate in handleConnect - Reject unauthorized connections early
6. Use noEcho - Prevent message echo back to sender when needed
When to Use
This skill is applicable to execute the workflow or actions described in the overview.
Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
The negotiate example requires real application authentication middleware; fail closed if identity is absent. Resolve user/group membership from the trusted server session, authorize every requested group and issue only group-specific roles with short expiry. Never grant a wildcard role while claiming group-scoped permissions. Tokens/connection URLs are bearer secrets; protect them in responses/logs and do not treat a connected socket as authorization for all groups. Scope broadcasts/group changes to the user’s approved recipients and content.
Related skills
- backup-recovery – Implement backup and recovery strategies. Configure rsync, Restic, and cloud backups…
- bash-linux – Bash/Linux terminal patterns. Critical commands, piping, error handling, scripting. Use…
- bash-pro – Master of defensive Bash scripting for production automation, CI/CD pipelines, and…
- hunt-subdomain – Hunting skill for subdomain takeover vulnerabilities. Includes modern provider…
- block-storage – Manage block storage volumes and LVM. Configure cloud block storage and local disks. Use…
- blue-green-deploy – Configure zero-downtime deployment strategies including blue-green, canary, and rolling…
- prometheus-configuration – Set up Prometheus for comprehensive metric collection, storage, and monitoring of…
- gcp-cloud-architect – Design GCP architectures for startups and enterprises. Use when asked to design Google…