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.
Attention: This sample runs with a deprecated service instance.
This sample is designed to work with a deprecated V1 machine learning service instance. It will fail with a V2 service instance, provisioned after September 1, 2020.
During the migration period, you can still run tutorials and examples associated with a legacy v1 Watson Machine Learning service instance. However, note the following requirements and restrictions:
- You must use a v1 service instance and associated credentials to run a v1 sample or example. Follow the authentication steps to authenticate with deprecated samples.
- Lite users can use existing v1 service credentials, but cannot create new credentials.
- Standard and Professional users can use existing v1 service credentials and can also create new v1 service credentials. The Credentials page was removed from the IBM Cloud Services catalog, so follow the steps in Generating legacy Watson Machine Learning credentials to create new credentials using the IBM Cloud CLI.
Prerequisites
-
Build and deploy the function in this tutorial:
-
Look up the endpoint URL of the model deployment and the function deployment from the previous prerequisite.
-
Collect these details from your Watson Machine Learning credentials:
apikey
instance_id
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:
- Download the sample Node.js app
- Update the code to use your deployments
- Run the app locally
- Push the app to IBM Cloud
- Remove hard-coded credentials
Step 1: Download the sample Node.js app
Download the sample code here: Nodejs-MNIST-sample-app.zip
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
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 ![]() |
package.json |
Lists packages and versions required by your app when you push the app to IBM Cloud. See also: npm-package.json ![]() |
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 ``` /*
-
- 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
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:
(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:
As you interact with the web app in the browser, you can see status information printed in the command window:
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:
After you click Analyze, you can see the web app creating the payload displayed in Generated payload:
- A rectangular bounding box is created around the digit (the red box is for illustration only, and is not part of the processing.)
- 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.
- The content in the square box is resized to 28 pixels by 28 pixels.
- The image is converted to gray scale by setting RGB values to zero, leaving only the Alpha value remaining.
- The Alpha values are collected in a 1 by 784 array.
- 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.
Results returned from the deployment are displayed in Returned classification:
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
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
- Give the app a unique name (for the rest of this example, the sample name will be: “fabulous-nodejs-app”)
- Accept the defaults in the other fields of the form
- Accept the 64 MB plan
- 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:
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
-
Log in to IBM Cloud dashboard
and then click View all in the Resource summary.
-
Expand the Cloud Foundry Apps section, and then click on your Node.js app to go to the details page for that app.
-
In the Connections section, click Create connection.
-
From the list of compatible services, click Connect beside the Watson Machine Learning service.
-
In the modal that appears, accept the defaults and then click Connect.
-
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: