Node.js tutorial: Build a web app that recognizes hand-drawn digits

This tutorial guides you through building a Node.js web app that uses a model trained with the MNIST data set to recognize digits that are hand drawn on an HTML canvas.

 

Prerequisites

  1. Build and deploy the function in this tutorial:

    MNIST function deployment tutorial

  2. Look up the endpoint URL of the model deployment and the function deployment from the previous prerequisite.

    See: Looking up an online deployment endpoint URL

  3. Collect these details from your Watson Machine Learning credentials:

    • apikey
    • instance_id

    See: Looking up credentials

 

Steps overview

This tutorial presents the basic steps for integrating a Watson Machine Learning model deployment or function deployment into a Node.js web app:

  1. Download the sample Node.js app
  2. Update the code to use your deployments
  3. Run the app locally
  4. Push the app to IBM Cloud
  5. Remove hard-coded credentials

 

Step 1: Download the sample Node.js app

Download the sample code here: Nodejs-MNIST-sample-app.zip external link

Then, unzip the sample code into a working directory. For the remainder of this tutorial, the term working directory refers to the directory containing the file server.js, and the examples here will show D:/Nodejs-MNIST-sample-app as the working directory.

Files in Nodejs-MNIST-sample-app.zip

Table 1. Files in Nodejs-MNIST-sample-app.zip
File Description
.cfignore When you push the app to IBM Cloud, any files and directories listed in .cfignore do not get uploaded.
manifest.yml Lists details about your app which are needed when you push the app to IBM Cloud. See also: Preparing your app for deployment external link
package.json Lists packages and versions required by your app when you push the app to IBM Cloud. See also: npm-package.json external link
server.js The Node.js server code that drives the functionality of your web app (for example: sending payload data to your model deployment or function deployment using the Watson Machine Learning REST API.)
public/index.html The HTML web page of your web app. Also includes Javascript for socket.io calls to your Node.js server.
public/css/styles.css Controls the style of objects in the HTML page (for example: font, color, size.)
public/javascript/javascript.js Implements callbacks of interface elements in the HTML web page (for example: button clicks.)

Preprocessing payload data

The function submit_drawing() in the file public/javascript/javascript.js illustrates an important comparison:

  • Sending canvas data to a deployed function, which preprocesses the canvas data and then sends the data to the deployed model
  • Preprocessing the canvas data in the web app front end and then sending that to a deployed model
    if( deployment_type.match( /function/ )  )
    {
      // If using the function deployment, we don't need to do any more
      // preprocessing here in the app because the function handles that
      var payload = { "values" : [ { "height" : centered_img.height, "data" : [ Array.from( centered_img.data ) ] } ] };
      ...
    }
    else
    {
      // To send a payload to the model deployment, we need to do
      // more preprocessing here in the app
      ...
      var tiny_img = resize_28x28( centered_img, box_X, box_Y );
      ...
      var gray_img = greyscale( tiny_img );    
      var arr      = create_array_28x28( gray_img );                    
      var payload  = { "values" : [ arr ] };
    

This illustrates how using deployed functions can simplify app development. On a team where one person specializes in app development and another person specializes in building AI models, it can be easier and more effective to have the data preprocessing done by the person familiar with the models.

 

Step 2: Update the code to use your deployments

Update code in the file server.js in a text editor.

2.1 Credentials

Paste in the Watson Machine Learning credentials you collected in the prerequisites for this tutorial:

/*
 * 1.  Fill in wml_credentials.
 *
 */
var wml_credentials = {
    "apikey"      : "",
    "instance_id" : ""
};

2.2 Deployment endpoint URL

Paste in the deployment endpoint URLs you collected in the prerequisites for this tutorial:

  • If you want to send payload data directly to a model deployment, paste in the endpoint URL for the deployed model
  • If you want to send payload data to a function deployment, paste in the endpoint URL for the deployed function ``` /*
    1. Fill in one or both of these:
    • model_deployment_endpoint_url
    • function_deployment_endpoint_url */ var model_deployment_endpoint_url = “”; var function_deployment_endpoint_url = “”; ```

 

Step 3: Run the app locally

Note: Running the app locally is not strictly required to be able to deploy the app to IBM Cloud. It’s a good idea to test that the app runs locally, and it’s easier to troubleshoot problems locally. However, you could skip step 3 and move on to deploying the sample app to IBM Cloud in step 4 (if you don’t want to install Node.js on your computer, for example.)

3.1 Install Node.js locally

Download Node.js here: Node.js download external link

3.2 Install required Node.js packages

Install the needed Node.js packages by issuing the following command from the working directory:

npm install

(This command uses the file package.json to know which packages are needed.)

3.3 Start the local server

From a command prompt in the working directory, start the server by issuing the following command:

node server.js

Output:

Node.js server started locally

(The server will start on localhost, but the specific port can vary. When you start the server on your computer, the port might not be 6029 like in this example.)

3.4 Open the web app in a browser

When the Node.js server starts, an address on localhost is listed. (For example, in the example output from the previous substep, the address is http://localhost:6029.) Open this address in a web browser:

Web app in a browser

As you interact with the web app in the browser, you can see status information printed in the command window:

Node.js server started locally

3.5 Test the web app locally

Test the web app by drawing a digit on the canvas, and then submitting the drawn digit to the model deployment or the function deployment:

Web app in a browser

After you click Analyze, you can see the web app creating the payload displayed in Generated payload:

  1. A rectangular bounding box is created around the digit (the red box is for illustration only, and is not part of the processing.)
  2. A square bounding box is created, with the digit centered in the box.

    If "Function deployment" is checked, processing stops here and this canvas data is sent to the function deployment.

  3. The content in the square box is resized to 28 pixels by 28 pixels.
  4. The image is converted to gray scale by setting RGB values to zero, leaving only the Alpha value remaining.
  5. The Alpha values are collected in a 1 by 784 array.
  6. Values in the array are normalized (divided by 255) to values between 0 and 1.

    If "Model deployment" is checked, this array is set to the model deployment.

Web app in a browser

Results returned from the deployment are displayed in Returned classification:

Web app in a browser

 

Step 4: Push the app to IBM Cloud

4.1 Install the IBM Cloud CLI

Download the IBM Cloud CLI here: IBM Cloud CLI download external link

4.2 Create a Node.js app in IBM Cloud

Create a Node.js Cloud Foundry app here: Create a Node.js Cloud Foundry app external link

  1. Give the app a unique name (for the rest of this example, the sample name will be: “fabulous-nodejs-app”)
  2. Accept the defaults in the other fields of the form
  3. Accept the 64 MB plan
  4. Click Create

4.3 Update local code for deployment to IBM Cloud

In <working-dir>/package.json replace “app-name” with the name you gave your new Node.js app in the previous step:

Before

{
  "name": "app-name",
  "main": "server.js",
  "description": "An app demonstrating using AI models to recognize hand-drawn digits",
...

After

{
  "name": "fabulous-nodejs-app",
  "main": "server.js",
  "description": "An app demonstrating using AI models to recognize hand-drawn digits",
...

In <working-dir>/manifest.yml replace “app-name” with the name you gave your new Node.js app in the previous step:

Before

applications:
 - name: app-name
   memory: 64M

After

applications:
 - name: fabulous-nodejs-app
   memory: 64M

In <working-dir>/public/index.html:

  • Replace “app-name” with the name you gave your new Node.js app in the previous step
  • Connect to the IBM Cloud route

Before

...
//var socket = io.connect( 'https://app-name.mybluemix.net/' );
var socket = io.connect();
...

After

var socket = io.connect( 'https://fabulous-nodejs-app.mybluemix.net/' );
//var socket = io.connect();

4.4 Push the app to IBM Cloud

Issue the following commands from the command prompt in your working directory:

Log in to your IBM Cloud account:

ibmcloud login

Target the CloudFoundry API endpoint:

ibmcloud target --cf

Push your app to IBM Cloud:

ibmcloud app push

Now, you can access the app on IBM Cloud at your app URL:

Web app on IBM Cloud

 

Step 5: Remove hard-coded credentials

In general, hard coding your Watson Machine Learning service credentials in your Node.js code is not a secure thing to do.

In IBM Cloud, you can Connect you apps and services (also called “binding a service to an application”), and then use the Cloud Foundry environment variable, VCAP_SERVICES, in your app to access service credentials.

5.1 Connect your Node.js app to your Watson Machine Learning service

  1. Log in to IBM Cloud dashboard external link and then click View all in the Resource summary.

  2. Expand the Cloud Foundry Apps section, and then click on your Node.js app to go to the details page for that app.

  3. In the Connections section, click Create connection.

  4. From the list of compatible services, click Connect beside the Watson Machine Learning service.

  5. In the modal that appears, accept the defaults and then click Connect.

  6. When prompted to restage your app, click Restage.

Now, in the Connections tab of your app details page in IBM Cloud, you can see your Watson Machine Learning service instance listed.

5.2 Update server.js to use VCAP_SERVICES

Comment out the credentials you hard coded in substep 2.1:

/*
 * 1.  Fill in wml_credentials.
 *
var wml_credentials = {
    "apikey"      : "",
    "instance_id" : ""
};
*/

Set wml_credentials using the Cloud Foundry environment, which was updated when you Connected your Watson Machine Learning service to your Node.js app in the previous substep:

// Cloud Foundry - needed to push/host our app on IBM Cloud ...
var cfenv  = require( 'cfenv' );
var appEnv = cfenv.getAppEnv();
var wml_credentials = appEnv.services["pm-20"][0]["credentials"];  // <-- Add this line
var server = app.listen( appEnv.port, appEnv.bind, function()
{
    console.log( 'Server starting on ' + appEnv.url );

} );

From the command prompt in your working directory, push your app to IBM Cloud again:

ibmcloud app push

Now, the app uses the credentials in the Cloud Foundry environment to authenticate with Watson Machine Learning.

See also: