Table of Contents for
Node.js 8 the Right Way

Version ebook / Retour

Cover image for bash Cookbook, 2nd Edition Node.js 8 the Right Way by Jim Wilson Published by Pragmatic Bookshelf, 2018
  1. Title Page
  2. Node.js 8 the Right Way
  3. Node.js 8 the Right Way
  4. Node.js 8 the Right Way
  5. Node.js 8 the Right Way
  6.  Acknowledgments
  7.  Preface
  8. Why Node.js the Right Way?
  9. What’s in This Book
  10. What This Book Is Not
  11. Code Examples and Conventions
  12. Online Resources
  13. Part I. Getting Up to Speed on Node.js 8
  14. 1. Getting Started
  15. Thinking Beyond the web
  16. Node.js’s Niche
  17. How Node.js Applications Work
  18. Aspects of Node.js Development
  19. Installing Node.js
  20. 2. Wrangling the File System
  21. Programming for the Node.js Event Loop
  22. Spawning a Child Process
  23. Capturing Data from an EventEmitter
  24. Reading and Writing Files Asynchronously
  25. The Two Phases of a Node.js Program
  26. Wrapping Up
  27. 3. Networking with Sockets
  28. Listening for Socket Connections
  29. Implementing a Messaging Protocol
  30. Creating Socket Client Connections
  31. Testing Network Application Functionality
  32. Extending Core Classes in Custom Modules
  33. Developing Unit Tests with Mocha
  34. Wrapping Up
  35. 4. Connecting Robust Microservices
  36. Installing ØMQ
  37. Publishing and Subscribing to Messages
  38. Responding to Requests
  39. Routing and Dealing Messages
  40. Clustering Node.js Processes
  41. Pushing and Pulling Messages
  42. Wrapping Up
  43. Node.js 8 the Right Way
  44. Part II. Working with Data
  45. 5. Transforming Data and Testing Continuously
  46. Procuring External Data
  47. Behavior-Driven Development with Mocha and Chai
  48. Extracting Data from XML with Cheerio
  49. Processing Data Files Sequentially
  50. Debugging Tests with Chrome DevTools
  51. Wrapping Up
  52. 6. Commanding Databases
  53. Introducing Elasticsearch
  54. Creating a Command-Line Program in Node.js with Commander
  55. Using request to Fetch JSON over HTTP
  56. Shaping JSON with jq
  57. Inserting Elasticsearch Documents in Bulk
  58. Implementing an Elasticsearch Query Command
  59. Wrapping Up
  60. Node.js 8 the Right Way
  61. Part III. Creating an Application from the Ground Up
  62. 7. Developing RESTful Web Services
  63. Advantages of Express
  64. Serving APIs with Express
  65. Writing Modular Express Services
  66. Keeping Services Running with nodemon
  67. Adding Search APIs
  68. Simplifying Code Flows with Promises
  69. Manipulating Documents RESTfully
  70. Emulating Synchronous Style with async and await
  71. Providing an Async Handler Function to Express
  72. Wrapping Up
  73. 8. Creating a Beautiful User Experience
  74. Getting Started with webpack
  75. Generating Your First webpack Bundle
  76. Sprucing Up Your UI with Bootstrap
  77. Bringing in Bootstrap JavaScript and jQuery
  78. Transpiling with TypeScript
  79. Templating HTML with Handlebars
  80. Implementing hashChange Navigation
  81. Listing Objects in a View
  82. Saving Data with a Form
  83. Wrapping Up
  84. 9. Fortifying Your Application
  85. Setting Up the Initial Project
  86. Managing User Sessions in Express
  87. Adding Authentication UI Elements
  88. Setting Up Passport
  89. Authenticating with Facebook, Twitter, and Google
  90. Composing an Express Router
  91. Bringing in the Book Bundle UI
  92. Serving in Production
  93. Wrapping Up
  94. Node.js 8 the Right Way
  95. 10. BONUS: Developing Flows with Node-RED
  96. Setting Up Node-RED
  97. Securing Node-RED
  98. Developing a Node-RED Flow
  99. Creating HTTP APIs with Node-RED
  100. Handling Errors in Node-RED Flows
  101. Wrapping Up
  102. A1. Setting Up Angular
  103. A2. Setting Up React
  104. Node.js 8 the Right Way

Handling Errors in Node-RED Flows

Although it’s great when everything goes according to plan, sometimes it doesn’t. One of the downsides of programming with Node-RED is that it can be difficult to track down problems when they occur. In this section, we’ll explore a few ways the book-search API from the last section might fail and how you can investigate and mitigate these failures.

Triggering an Error

To begin, let’s see what happens when the API caller omits the q query parameter. In your terminal, use curl with the -i and -v flags like so to include HTTP headers and produce verbose output:

 $ ​​curl​​ ​​-i​​ ​​-v​​ ​​localhost:1880/search
 * Trying 127.0.0.1...
 * Connected to localhost (127.0.0.1) port 1880 (​#0)
 > GET /search HTTP/1.1
 > Host: localhost:1880
 > User-Agent: curl/7.47.0
 > Accept: */*
 >

From this output, we can see that Node-RED, listening on port 1880, received the underlying TCP connection and then curl issued an HTTP request for /search. But after that, we hear nothing back from Node-RED, not even HTTP headers. What’s going on?

To find out, head back to your web browser and take a look in the Debug tab on the right-hand side. You should see an error reading, "TypeError: Cannot read property ’hits’ of undefined". When you hover over this error, the node that produced it, the function node, will have an orange dashed outline in the flow editor.

images/node-red-http-debugging-function-hits.png

The problem seems to be with the input coming into the function node. From the error text, it seems like there’s a problem in some part of the expression msg.payload.hits.hits. Let’s use a debug node to investigate.

Investigating an Error

To investigate the error triggered in the last section, grab a debug node from the nodes panel and drag it onto the flow editor. Attach the debug node to the output of the Elasticsearch HTTP request node.

images/node-red-debugging-cannot-read.png

This will log to the Debug tab what’s in the payload the next time Node-RED receives a request. Deploy this flow, then head back to your terminal.

Use Ctrl-C to kill the previous curl command, and then run it again. It should produce the same behavior as before, but now when you switch back to your web browser, you should see an expandable payload object in the Debug tab.

images/node-red-http-investigating-with-debug.png

When you hover over the debug output with your mouse, the debug node will have a dashed red outline. Digging into the msg.payload object in the debug console, you’ll find that there is no payload.hits field at all. Instead, the payload has an error field with a type of illegal_argument_exception and a reason of [match_phrase] requires fieldName.

That’s strange because, as you’ll recall, part of the prep query node’s job is to set the match_phrase.title field. Let’s attach the debug node to the prep query output and take a look at that next. You’ll probably want to clear the debug output by clicking the trash can button in the top right.

After you deploy the change, run curl again, then flip back to the web browser. You should see more debug output this time, including an entry for the output of the prep query node.

images/node-red-http-missing-match-phrase-title.png

Expanding the prep query output, notice that the match_phrase field is an empty object. Its title field is missing!

And here’s one of the nitty-gritty details about working with Node-RED. The prep query change node you created earlier has two rules. The first rule succeeds in setting the msg.payload.query object to the JSON {"match_query":{}}. But the second rule fails to move the msg.payload.q property because that property doesn’t exist!

So let’s fix it.

Handling Errors Generically

At this point, your HTTP flow fails when the request lacks a q parameter, and by debugging it you have a pretty good idea of the root cause. There are a few ways to mitigate the problem:

  • Detect the absence of a q parameter early and inform the API caller with an HTTP 400 Bad Request response.

  • Detect the absence of a hits field in the Elasticsearch results and skip the extract-titles step.

  • Detect any error at all and return an HTTP 500 Server Error response to the API caller.

These approaches aren’t mutually exclusive. Let’s start with the most general approach—returning a 500 Server Error.

In the nodes panel, find the catch node under inputs. Drag one onto the flow editor. Next, drag an HTTP response node onto the editor and connect it to the catch node.

images/node-red-http-catch-all.png

Strictly speaking, you could reuse the existing HTTP response node rather than bringing in a new one. But I find it more aesthetically pleasing to have individual HTTP response nodes for each different way the flow could terminate.

Once you deploy the flow, any unhandled error condition in the flow will trigger the catch node. If the msg object has an HTTP response object associated with it, then the attached HTTP response node will reply to the API caller.

Let’s try it out in the terminal. This time, let’s pipe the output of curl into jq. If you need a refresher on jq, flip back to Shaping JSON with jq.

 $ ​​curl​​ ​​-s​​ ​​localhost:1880/search​​ ​​|​​ ​​jq​​ ​​'.'
 {
  "error": {
  "root_cause": [
  {
  "type": "illegal_argument_exception",
  "reason": "[match_phrase] requires fieldName"
  }
  ],
  "type": "illegal_argument_exception",
  "reason": "[match_phrase] requires fieldName"
  },
  "status": 400
 }

Great! At least the call to curl no longer hangs indefinitely.

Let’s make one more improvement to the flow by detecting a bad request before handing it over to Elasticsearch.

Catching Errors Early

At this point, your flow no longer hangs indefinitely if the caller omits the q parameter. This is a big improvement, but we can do even better.

If the API caller didn’t supply a q parameter, then there’s no reason to reach all the way out to Elasticsearch for results. We should be able to report an HTTP 400 Bad Request response to the API caller directly.

To do this, make some room in your flow by splitting the [get] /search input node into its own column. This will leave space below for the q-parameter-checking nodes.

images/node-red-http-making-space.png

Next, in the nodes panel, find the switch node in the function section. Drag a switch node onto the flow editor under the [get] /search node. The switch node can be used to route a message along different paths, much like a switch statement in JavaScript.

To configure it, double-click the switch node to open the Edit dialog. Set the Name field to q? to indicate that we’re testing for the q parameter. For the Property field, set it to msg.payload.q.

Below the Name and Property fields is an area where you can specify routing rules. In the first rule, click the drop-down and select is not null.

Next, click the +add button to insert a second rule. For this rule, click the drop-down and select otherwise.

Lastly, in the final drop-down at the bottom, choose stopping after first match. When you’re done, the Edit dialog should look like this:

images/node-red-http-switch-edit-dialog.png

After you click Done, the switch node should now have two output ports—one for each of the two conditions, in order from top to bottom. Since the first output was for the is not null case, we want to wire that into the prep query node. The input to the switch node should be the output of the HTTP input node above it.

For the other switch case, we’ll need a few more nodes, starting with a change node to set up the HTTP 400 Bad Request response. Drag a change node onto the flow under the switch node, connect its input to the second switch output, then double-click the change node to open the Edit dialog.

Set the change node’s name to 400. In the Rules section, first set the msg.payload to the JSON string {"error":"Query string ’q’ missing."}. Then add a second rule and set the msg.statusCode to be the number 400. The HTTP response node will use this to return a 400 Bad Request status code.

When you’re finished, the node editor should look like the following:

images/node-red-http-400-edit-dialog.png

After you click Done to dismiss the dialog, all that remains is to add another HTTP response node underneath the 400 change node and wire it up. When you deploy the flow, it should look like the following:

images/node-red-http-final-flow.png

Time to try it all out. In your terminal, use curl to see how the flow deals with a missing q parameter.

 $ ​​curl​​ ​​-s​​ ​​localhost:1880/search​​ ​​|​​ ​​jq​​ ​​'.'
 {
  "error": "Query string 'q' missing."
 }

Success! Now let’s reconfirm that it still returns results for expected queries.

 $ ​​curl​​ ​​-s​​ ​​localhost:1880/search?q=juliet​​ ​​|​​ ​​jq​​ ​​'.'
 [
  "Romeo and Juliet",
  "The Indifference of Juliet",
  "Romeo and Juliet",
  "Romeo and Juliet",
  "Romeo and Juliet",
  "The Tragedy of Romeo and Juliet",
  "Shakespeare's Tragedy of Romeo and Juliet"
 ]

At this point you have a functioning and error-resistant HTTP API endpoint. Now let’s wrap up.