Microsoft Bot Framework Direct Line JS Client
This package provides a client library for the Microsoft Bot Framework Direct Line 3.0 protocol, enabling JavaScript applications to communicate directly with bots. It is an official Microsoft-supported library, used internally by components like BotFramework-WebChat, the Bot Framework Emulator, and Azure Bot Service. The current stable version is 0.15.8. The library primarily uses RxJS Observables for handling asynchronous operations, a key differentiator from Promise-based alternatives. While considered largely complete for its protocol, updates are typically limited to dependency bumps, bug fixes, and minor enhancements rather than new feature development, indicating a maintenance-focused release cadence. It fully supports TypeScript.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'subscribe')
cause Attempting to use `activity$` or `connectionStatus$` as a Promise or regular value, when they are RxJS Observables.fixEnsure you call `.subscribe()` on the Observable to initiate the stream and receive values. Example: `directLine.activity$.subscribe(activity => { /* handle activity */ });` -
DirectLine error: token expired.
cause The authentication token used to establish the Direct Line connection has become invalid or has expired.fixWhen `connectionStatus$` reports `ConnectionStatus.ExpiredToken`, your application should request a new token from your backend service and re-initialize or reconnect the `DirectLine` object with the fresh token. -
WebSocket connection to 'wss://directline.botframework.com/v3/directline/conversations/...' failed: WebSocket is closed before the connection is established.
cause Often indicates an invalid or expired Direct Line secret/token, or a network issue preventing the initial WebSocket handshake.fixVerify that your `secret` or `token` is correct and active. If using a secret, ensure it hasn't been revoked. Check network connectivity and firewall rules. For production, always use a generated token from a secure backend. -
Silent message loss or unresponsive bot on iOS/iPadOS after network changes.
cause The device's WebSocket connection silently stalls without error, as detailed in the `networkInformation` warning.fixImplement the `networkInformation` polyfill to detect network changes and proactively manage the Direct Line connection. Reloading the page or re-initializing DirectLine may temporarily resolve it.
Warnings
- gotcha The library utilizes RxJS Observables for all asynchronous operations (e.g., `activity$`, `connectionStatus$`). Developers unfamiliar with RxJS may encounter difficulties when expecting Promise-based or callback-style asynchronous patterns.
- gotcha On iOS/iPadOS 15+, the `WebSocket` object may stall without errors when the network changes (e.g., Wi-Fi to cellular) due to an experimental 'NSURLSession WebSocket' feature. This prevents the library from detecting disconnections automatically.
- gotcha Direct Line secrets should *never* be exposed in client-side code for production applications. They grant full access to your bot's Direct Line channel.
- deprecated Older versions of the library, particularly before 0.11.5, used different build output directories (`/dist/directLine.js` vs `/dist/directline.js`). While recent versions standardize `/lib/` for ESM and types, older client code might expect specific paths.
Install
-
npm install botframework-directlinejs -
yarn add botframework-directlinejs -
pnpm add botframework-directlinejs
Imports
- DirectLine
const DirectLine = require('botframework-directlinejs');import { DirectLine } from 'botframework-directlinejs'; - ConnectionStatus
import ConnectionStatus from 'botframework-directlinejs';
import { ConnectionStatus } from 'botframework-directlinejs'; - Activity
import { Activity } from 'botframework-directlinejs';
Quickstart
import { DirectLine, ConnectionStatus, Activity } from 'botframework-directlinejs';
import { filter } from 'rxjs/operators';
// In a real application, retrieve this securely (e.g., from an environment variable or a server-side call)
const DIRECT_LINE_SECRET = process.env.DIRECT_LINE_SECRET ?? 'YOUR_DIRECT_LINE_SECRET_HERE';
const USER_ID = 'user123'; // A unique user ID for the conversation
console.log('Starting Direct Line client...');
const directLine = new DirectLine({
secret: DIRECT_LINE_SECRET, // For production, exchange secret for a token via a backend service
webSocket: true, // Use Web Sockets for real-time communication
// token: 'YOUR_DIRECT_LINE_TOKEN_HERE' // Recommended for production
});
// Observe connection status changes
directLine.connectionStatus$.subscribe(connectionStatus => {
const statusMessage = ConnectionStatus[connectionStatus];
console.log(`Connection status: ${statusMessage}`);
if (connectionStatus === ConnectionStatus.Uninitialized) {
console.warn('Direct Line connection is uninitialized. Check your secret/token.');
} else if (connectionStatus === ConnectionStatus.ExpiredToken) {
console.error('Direct Line token expired. Reconnect with a new token.'); //
}
});
// Subscribe to incoming activities from the bot
const activitySubscription = directLine.activity$
.pipe(
filter(activity => activity.type === 'message' && activity.from.id !== USER_ID)
)
.subscribe(activity => {
console.log(`Received activity from bot:`);
console.log(JSON.stringify(activity, null, 2));
if (activity.text && activity.text.toLowerCase().includes('hello')) {
console.log('Bot greeted us back!');
}
}, error => {
console.error('Error receiving activities:', error);
});
// Send a message to the bot after a brief delay
setTimeout(() => {
const message: Activity = {
from: { id: USER_ID, name: 'Test User' },
type: 'message',
text: 'Hello bot!',
channelData: { clientActivityID: Date.now().toString() } // Unique ID for client activity
};
directLine.postActivity(message).subscribe(
id => console.log(`Sent activity with ID: ${id}`),
error => console.error('Error sending activity:', error)
);
}, 3000);
// Disconnect after some time (e.g., when the user leaves the chat)
setTimeout(() => {
console.log('Ending conversation and disconnecting...');
activitySubscription.unsubscribe();
directLine.end(); // Clean up Direct Line connection
}, 15000); // Disconnect after 15 seconds for this example