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

Writing Modular Express Services

Throughout the remainder of the chapter, we’re going to build a RESTful web service with Express for creating and managing book bundles. These are basically named reading lists. Here’s an example of a book bundle:

 {
 "name"​: ​"light reading"​,
 "books"​: [{
 "id"​: ​"pg132"​,
 "title"​: ​"The Art of War"
  },{
 "id"​: ​"pg2680"​,
 "title"​: ​"Meditations"​,
  },{
 "id"​: ​"pg6456"​,
 "title"​: ​"Public Opinion"
  }]
 }

The name field is a user-defined string to identify the list. Names do not have to be unique. The books field contains a list of the books in that bundle. Each book is identified by its document ID and includes the title of the book.

Our app will be called Better Book Bundle Builder (or B4 for short).

We’ll work extensively with the books index that we set up in Chapter 6, Commanding Databases, as well as a second Elasticsearch index called b4. The application will work roughly as follows:

  • It will communicate with two indices: the books index and the b4 index.

  • To the B4 application, the books index is read-only (we will not add, delete, or overwrite any documents in it).

  • The b4 index will store user data, including the book bundles that users make.

To create the b4 index, make sure Elasticsearch is running, then open a terminal to the esclu directory from the last chapter. Use esclu to create the b4 index:

 $ ​​./esclu​​ ​​create-index​​ ​​-i​​ ​​b4
 {"acknowledged":true,"shards_acknowledged":true}

Now we’re ready to create our modular, RESTful web services.

Separating Server Code into Modules

Just like our Hello World example, the main entry point for the b4 service is the server.js file. But instead of assigning a handler with app.get() directly, now we’ll specify some configuration parameters and then pull in API modules.

To start, create a directory called b4 to house the B4 project. Next, use npm init to create the basic outline of a package.json. All of the default values are fine.

 $ ​​mkdir​​ ​​b4
 $ ​​cd​​ ​​b4
 $ ​​npm​​ ​​init

Now install the Express, Morgan, and nconf modules. We’ll use the nconf module to manage configuration settings like the hostname and port of the Elasticsearch server.

 $ ​​npm​​ ​​install​​ ​​--save​​ ​​--save-exact​​ ​​express@4.14.1​​ ​​morgan@1.8.1​​ ​​nconf@0.8.4

OK, next comes the configuration settings. Open your text editor and enter the following:

 {
 "port"​: 60702,
 "es"​: {
 "host"​: ​"localhost"​,
 "port"​: 9200,
 "books_index"​: ​"books"​,
 "bundles_index"​: ​"b4"
  }
 }

Save this file as config.json. It contains the port number that Express will listen on as well as information about where to find the Elasticsearch service.

Finally, let’s put together the shell of the server.js. Open your editor and enter this:

 'use strict'​;
 const​ express = require(​'express'​);
 const​ morgan = require(​'morgan'​);
 const​ nconf = require(​'nconf'​);
 const​ pkg = require(​'./package.json'​);
 
 nconf.argv().env(​'__'​);
 nconf.defaults({conf: ​`​${__dirname}​/config.json`​});
 nconf.file(nconf.​get​(​'conf'​));
 
 const​ app = express();
 
 app.use(morgan(​'dev'​));
 
 app.​get​(​'/api/version'​, (req, res) => res.status(200).send(pkg.version));
 
 app.listen(nconf.​get​(​'port'​), () => console.log(​'Ready.'​));

Aside from the nconf setup, the content of this file should seem pretty familiar to you. Putting the nconf part aside for just a moment, let’s step through the rest of the code.

First, as usual, we pull in the modules we depend on. We’ll also pull in the package.json contents so we can put out a simple version endpoint.

After setting up nconf, we invoke express to create the app object. With it, we add Morgan for logging, then establish a simple endpoint at the path /api/version that reports the version listed in package.json.

Finally, we instruct the Express app to listen on our configured port (60702). Let’s start this up and try it out; then we’ll return to nconf.

 $ ​​npm​​ ​​start
 
 > b4@1.0.0 start ./code/web-services/b4
 > node server.js
 
 Ready.

To test it, use curl to hit the /api/version endpoint.

 $ ​​curl​​ ​​-s​​ ​​"http://localhost:60702/api/version"
 1.0.0

Great! Looks like everything is working so far. Now let’s dig into how nconf manages configuration settings.

The nconf module manages configuration settings through a customizable hierarchy of config files, environment variables, and command-line arguments. The order in which you load a source of configuration determines its precedence. Earlier values stick, meaning that later values will not overwrite them. Here again is the first line of the server.js file that handles setting up nconf:

 nconf.argv().env(​'__'​);

This first line means that nconf should load argument variables first, then environment variables. The double underscore string passed to env means that two underscores should be used to denote object hierarchy when reading from environment variables. This is because many shell programs do not allow colon characters in variable names.

An example should help clarify. In the config.json file, recall that we set es.host to localhost. nconf uses the colon character by default to flatten the object hierarchy, so to get the value of this configuration parameter, we would call nconf.get(’es:host’) in Node.

Now, we’ve set it up so that nconf gives us the option to override es:host either as an argument variable or as an environment variable since these are loaded first. To override es:host as a command-line argument, we would invoke server.js from the command line like this:

 $ ​​node​​ ​​server.js​​ ​​--es:host=some.other.host

On the other hand, to override es:host with an environment variable, we would invoke server.js like so:

 $ ​​es__host=some.other.host​​ ​​node​​ ​​server.js

Now let’s move on to the second line of the nconf setup code.

 nconf.defaults({conf: ​`​${__dirname}​/config.json`​});

This line establishes a default value for the conf parameter. This is the path to the configuration file we created earlier. But since we’re prioritizing environment variables and command-line arguments, the user could employ either of those mechanisms to override the configuration file path.

For example, to override the config file path using a command-line argument, you could do this:

 $ ​​node​​ ​​server.js​​ ​​--conf=/path/to/some.other.config.json

Finally, in the last line of the nconf setup stanza, we tell nconf to load the file defined in the conf path.

 nconf.file(nconf.​get​(​'conf'​));

Any values in that file will take effect only if they haven’t been set already in the command-line arguments or environment variables.

These three lines give you some pretty amazing flexibility right out of the gate. Of course, you could choose to call the argv, env, file, and other methods in a different order to achieve different effects. Check out the nconf npm page for more info.[67]