Manage state data

The Bot Builder Framework enables your bot to store and retrieve state data that is associated with a user, a conversation, or a specific user within the context of a specific conversation. State data can be used for many purposes, such as determining where the prior conversation left off or simply greeting a returning user by name. If you store a user's preferences, you can use that information to customize the conversation the next time you chat. For example, you might alert the user to a news article about a topic that interests them, or alert a user when an appointment becomes available.

For testing and prototyping purposes, you can use the Bot Builder Framework's in-memory data storage. For production bots, you can implement your own storage adapter or use one of Azure Extensions. The Azure Extensions allow you to store your bot's state data in either Table Storage, CosmosDB, or SQL. This article will show you how to use the in-memory storage adapter to store your bot's state data.

Important

The Bot Framework State Service API is not recommended for production environments, and may be deprecated in a future release. It is recommended that you update your bot code to use the in-memory storage adapter for testing purposes or use one of the Azure Extensions for production bots.

In-memory data storage

In-memory data storage is intended for testing only. This storage is volatile and temporary. The data is cleared each time the bot is restarted. To use the in-memory storage for testing purposes, you will need to do two things. First create a new instance of the in-memory storage:

var inMemoryStorage = new builder.MemoryBotStorage();

Then, set it to the bot when you create the UniversalBot:

var inMemoryStorage = new builder.MemoryBotStorage();
var bot = new builder.UniversalBot(connector, [..waterfall steps..])
                    .set('storage', inMemoryStorage); // Register in-memory storage 

You can use this method to set your own custom data storage or use any of the Azure Extensions.

Manage custom data storage

For performance and security reasons in the production environment, you may implement your own data storage or consider implementing one of the following data storage options:

  1. Manage state data with Cosmos DB

  2. Manage state data with Table storage

With either of these Azure Extensions options, the mechanism for setting and persisting data via the Bot Framework SDK for Node.js remains the same as the in-memory data storage.

Storage containers

In the Bot Builder SDK for Node.js, the session object exposes the following properties for storing state data.

Property Scoped to Description
userData User Contains data that is saved for the user on the specified channel. This data will persist across multiple conversations.
privateConversationData Conversation Contains data that is saved for the user within the context of a particular conversation on the specified channel. This data is private to the current user and will persist for the current conversation only. The property is cleared when the conversation ends or when endConversation is called explicitly.
conversationData Conversation Contains data that is saved in the context of a particular conversation on the specified channel. This data is shared with all users participating in the conversation and will persist for the current conversation only. The property is cleared when the conversation ends or when endConversation is called explicitly.
dialogData Dialog Contains data that is saved for the current dialog only. Each dialog maintains its own copy of this property. The property is cleared when the dialog is removed from the dialog stack.

These four properties correspond to the four data storage containers that can be used to store data. Which properties you use to store data will depend upon the appropriate scope for the data you are storing, the nature of the data that you are storing, and how long you want the data to persist. For example, if you need to store user data that will be available across multiple conversations, consider using the userData property. If you need to temporarily store local variable values within the scope of a dialog, consider using the dialogData property. If you need to temporarily store data that must be accessible across multiple dialogs, consider using the conversationData property.

Data Persistence

By default, data that is stored using the userData, privateConversationData, and conversationData properties is set to persist after the conversation ends. If you do not want the data to persist in the userData container, set the persistUserData flag to false. If you do not want the data to persist in the conversationData container, set the persistConversationData flag to false.

// Do not persist userData
bot.set(`persistUserData`, false);

// Do not persist conversationData
bot.set(`persistConversationData`, false);

Note

You cannot disable data persistence for the privateConversationData container; it is always persisted.

Set data

You can store simple JavaScript objects by saving them directly to a storage container. For a complex object like Date, consider converting it to string. This is because state data is serialized and stored as JSON. The following code samples show how to store primitive data, an array, an object map, and a complex Date object.

Store primitive data

session.userData.userName = "Kumar Sarma";
session.userData.userAge = 37;
session.userData.hasChildren = true;

Store an array

session.userData.profile = ["Kumar Sarma", "37", "true"];

Store an object map

session.userData.about = {
    "Profile": {
        "Name": "Kumar Sarma",
        "Age": 37,
        "hasChildren": true
    },
    "Job": {
        "Company": "Contoso",
        "StartDate": "June 8th, 2010",
        "Title": "Developer"
    }
}

Store Date and Time

For a complex JavaScript object, convert it to a string before saving to storage container.

var startDate = builder.EntityRecognizer.resolveTime([results.response]); 

// Date as string: "2017-08-23T05:00:00.000Z" 
session.userdata.start = startDate.toISOString(); 

Saving data

Data that is created in each storage container will remain in memory until the container is saved. The Bot Builder SDK for Node.js sends data to the ChatConnector service in batches to be saved when there are messages to be sent. To save the data that exists in the storage containers without sending any messages, you can manually call the save method. If you do not call the save method, the data that exists in the storage containers will be persisted as part of the batch processing.

session.userData.favoriteColor = "Red";
session.userData.about.job.Title = "Senior Developer"; 
session.save();

Get data

To access the data that is saved in a particular storage container, simply reference the corresponding property. The following code samples show how to access data that was previously stored as primitive data, an array, an object map, and a complex Date object.

Access primitive data

var userName = session.userData.userName;
var userAge = session.userData.userAge;
var hasChildren = session.userData.hasChildren;

Access an array

var userProfile = session.userData.userProfile;

session.send("User Profile:");
for(int i = 0; i < userProfile.length, i++){
    session.send(userProfile[i]);
}

Access an object map

var about = session.userData.about;

session.send("User %s works at %s.", about.Profile.Name, about.Job.Company);

Access a Date object

Retrieve date data as string then convert it into a JavaScript's Date object.

// startDate as a JavaScript Date object. 
var startDate = new Date(session.userdata.start); 

Delete data

By default, data that is stored in the dialogData container is cleared when a dialog is removed from the dialog stack. Likewise, data that is stored in the conversationData container and privateConversationData container is cleared when the endConversation method is called. However, to delete data stored in the userData container, you have to explicitly clear it.

To explicitly clear the data that is stored in any of the storage containers, simply reset the container as shown in the following code sample.

// Clears data stored in container.
session.userData = {}; 
session.privateConversationData = {};
session.conversationData = {};
session.dialogData = {};

Never set a data container null or remove it from the session object, as doing so will cause errors the next time you try to access the container. Also, you may want to manually call session.save(); after you manually clear a container in memory, to clear any corresponding data that has previously been persisted.

Next steps

Now that you understand how to manage user state data, let's take a look at how you can use it to better manage conversation flow.

Additional resources