title: Best way to support multiple users in a NodeJS based system?
body: <p>I'm creating a chatbot in Facebook messenger.  I want the chatbot to be able to support several users, obviously.  The first time a user accesses the chatbot they are supposed to create a user account.  The process for creating the user account is as follows:</p>
  
  <ol>
  <li>When an unregistered user interacts with the chatbot, it gets the user's first name from the Facebook Graph API, and then says "Hello (first name)".  It explains that the user needs to create a user profile.</li>
  <li>The chatbot asks the user what they want to be called.  The options are either (first name) or "Something else"</li>
  <li>If the user chooses (first name), the chatbot goes straight to number (5).</li>
  <li>If the user chooses "Something else", the chatbot asks what they want to be called, and they type it in.</li>
  <li>The chatbot asks the user to select their gender (male, female, other, prefer not to say)</li>
  <li>The chatbot asks the user to select their age (from a selection of ranges e.g. 18-25, 26-30 etc.)</li>
  <li>Once the profile has been created the chatbot tells the user that in future if they want to talk to the chatbot, they can just say "hi" or "hello".</li>
  <li>The chatbot then asks the user what they want to do now and presents the main functions.</li>
  </ol>
  
  <p>Here is what is supposed to happen normally when any user interacts with the chatbot:</p>
  
  <ol>
  <li>User interacts with chatbot.</li>
  <li>Chatbot checks to see if user is loaded into memory</li>
  <li>If user is loaded into memory, chatbot interacts with user as normal</li>
  <li>If user is not loaded, the chatbot tries to get the user from a database</li>
  <li>If the user exists in the database, the user's profile is loaded into memory and the chatbot interacts with the user as normal</li>
  <li>If the user doesn't exist in the database, they have never used the chatbot before, so they have to create a user profile.</li>
  </ol>
  
  <p>The problem is, this ISN'T happening.  When more than one user tries to set up their profile at the same time, the following is happening (let's assume two users - "user A" and "user B":</p>
  
  <p>For person A:</p>
  
  <ol>
  <li><p>The chatbot gets the user's first name, and says "Hello (person A)", and explains that they need to create a user profile.</p></li>
  <li><p>The chatbot then asks the user what they want to be called.  The options it gives are either (person B) or "Something else".</p></li>
  <li><p>No matter what the user chooses, they cannot progress any further,  because the chatbot keeps repeating stage 2.</p></li>
  </ol>
  
  <p>So, my code:</p>
  
  <p>User data is stored in a "data" object in a file called <code>user.js</code>:</p>
  
  <pre><code>data: {
      fbID: undefined,    // senderID (user's Facebook PSID)
      firstName: undefined,   // User's first name (only used during user profile creation)
      userID: undefined,  // User ID (i.e. first name or nickname)
      gender: undefined,  // Gender
      age: undefined, //  Age
  }
  </code></pre>
  
  <p>User.js also contains a "reload" function that is used to reload a user.  You'll see how this works later:</p>
  
  <pre><code>reload: function(data) {
      self.data.fbID = data.fbid; /** @namespace data.fbid */
      self.data.userID = data.userid; /** @namespace data.userid */
      self.data.gender = data.gender; /** @namespace data.gender */
      self.data.age = data.age;   /** @namespace data.age */
      console.log("Profile reloaded for user '%s'.", self.data.fbID);
  },
  </code></pre>
  
  <p>When the user sends a message to the chatbot, this triggers a webhook event, within <code>app.js</code>:</p>
  
  <pre><code>// Accept POST requests to webhook
  app.post('/webhook/', function (req, res) {
      // Parse the request body from the POST
      let body = req.body;
      // Check the webhook event is from a Page subscription
      if (body.object === 'page') {
          body.entry.forEach(function(entry) {     /** @namespace body.entry */
              // Gets the body of the webhook event
              let webhookEvent = entry.messaging[0];      /** @param entry.messaging[] */
              // Get sender ID
              let senderID = webhookEvent.sender.id;
              // Set session ID
              session.set(senderID);
              // Handle webhook event
              handler.handleWebhookEvent(senderID, webhookEvent);
          });
          // Return a '200 OK' response to all events
          res.status(200).send('EVENT_RECEIVED');
      } else {
          // Return a '404 Not Found' if event is not from a page subscription
          res.sendStatus(404);
      }
  });
  </code></pre>
  
  <p>The webhook event is passed to <code>handler.js</code>, where it is handled by the <code>handleWebhookEvent</code> function:</p>
  
  <pre><code>handleWebhookEvent: function(senderID, webhookEvent) {
      let greetingText, delay;
      let handleEvent = function() {
          /**
           * @param webhookEvent
           * @param webhookEvent.message
           * @param webhookEvent.optin
           * @param webhookEvent.delivery
           * @param webhookEvent.postback
           * @param webhookEvent.read
           * @param webhookEvent.account_linking
           * */
          if (webhookEvent.optin) {
              handleOptIn(webhookEvent);
          } else if (webhookEvent.message) {
              handleMessage(senderID, webhookEvent.message);
          } else if (webhookEvent.delivery) {
              handleDelivery(webhookEvent);
          } else if (webhookEvent.postback) {
              handlePostback(senderID, webhookEvent.postback);
          } else if (webhookEvent.read) {
              handleRead(webhookEvent);
          } else if (webhookEvent.account_linking) {
              handleAccountLink(senderID, webhookEvent.account_linking);
          } else {
              console.log("Webhook received unknown event: ", webhookEvent);
          }
      };
      if (common.isDefined(user.data.fbID) &amp;&amp; user.data.fbID === senderID) { 
          // User is loaded - handle events as normal
          console.log("User '%s' is loaded.", senderID);
          //logging.logEvent(senderID, webhookEvent, function() {
              handleEvent();
          //});
      } else {    // User is not loaded
          getUser(senderID, function(result) {   // First try and load user
              if (common.isDefined(result)) { // Result returned
                  user.reload(result);
                  //logging.logEvent(senderID, webhookEvent, function() {
                      handleEvent();
                  //});
              } else {    
                  // User was not found in database.  We need to 
                  create a new profile for the user
                  common.getFBUser(senderID, function(name) {
                      console.log("User '%s' was not found in database.", senderID);
                      user.data.fbID = senderID;
                      user.data.firstName = name; // Stores user's first name 
                      in the user object
                      greetingText = strings.initialGreeting.replace("%USER", 
                      user.data.firstName);
                      delay = common.setDelay(greetingText);
                      messaging.sendTextMessage(senderID, greetingText);
                      setTimeout(function() {
                          // Pause before kicking off the 'uprofile' intent
                          dialogflow.sendRequest(session.getIDs(), senderID, 
                          strings.createUser);
                      }, delay);
                  });
              }
          });
      }
  }
  </code></pre>
  
  <p>I think the issue here is that I thought that by using an object within <code>user.js</code>, the chatbot would just create multiple instances of this object, one for each user.  Clearly I'm mistaken.  What would be the best way to create multiple instances and thus prevent a situation where the user data object is being shared by multiple users?  Or should I just dispense with objects entirely and simply read from the database each time?  I want to avoid doing this as it will require a lot of reads/writes.</p>