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

Setting Up Passport

Passport provides authentication middleware for Express-based applications. We’re going to use it to implement social sign-on using three popular platforms: Facebook, Twitter, and Google. All three of these offer an OAuth flow for federated authentication, and there are Passport plugins to help simplify the configuration. Even with Passport’s help, configuring all of these services takes some finesse.

Of course, you can use Passport to implement regular old username/password authentication as well, but this introduces new complications, like where to store user identities, how to encrypt passwords, how to support users who have forgotten their passwords, etc. Since users frequently already have an account with at least one authentication provider like Facebook, Twitter, or Google, social sign-in is an increasingly popular way to outsource these concerns.

The basic setup of Passport for your application is the same either way. Let’s get Passport installed and configured, then use it to connect to the authentication providers.

Installing and Configuring Passport

To start, install the Passport Node.js module.

 $ ​​npm​​ ​​install​​ ​​--save​​ ​​-E​​ ​​passport@0.4.0

Next, open your server.js file for editing. After the part on configuring Express sessions that you added earlier, insert this new Passport setup section.

 // Passport Authentication.
 const​ passport = require(​'passport'​);
 passport.serializeUser((profile, done) => done(​null​, {
  id: profile.id,
  provider: profile.provider,
 }));
 passport.deserializeUser((user, done) => done(​null​, user));
 app.use(passport.initialize());
 app.use(passport.session());

Passport requires you to implement methods to serialize and deserialize users. That is, you have to tell Passport how to go from a user’s identity token to an actual user object and vice versa. Typically, this is where you’d reach out to a database of some kind to load up an account object based on the user’s ID. Both serializeUser and deserializeUser take a single callback function.

The callback function you pass to serializeUser should take a Passport User Profile object,[92] and call done with the minimum amount of data necessary to identify that user. For example, this function could query a database, then return a simple user ID string. In our case, we’re not going to store per-user data in a database (aside from book bundles, which we’ll get to), so all we need is to keep track of the user’s ID and which provider the user authenticated with. This is sufficient to uniquely identify a user in our system.

The deserializeUser method takes a function that does the opposite. Given the identifier produced by your serializeUser callback, your deserializeUser function should retrieve the full object. In our case, the identifier is an object with the id of the user and the provider the user signed in with. Since this is all we know about the user, there’s no need to perform any kind of lookup and we can just send this object to done straight away. For more info on how to implement these callbacks, check out Passport’s session documentation.[93]

After that, we call app.use on the output of passport.initialize and passport.session. Order is important here! The passport.session middleware must come after the expressSession that you added earlier. Otherwise sessions may not be restored properly.

The next step is to set up a couple of generic session routes: /auth/session for info about the session and /auth/signout to allow users to sign off. Let’s add those.

Adding Session Routes

We’ll need two Express routes irrespective of which authentication provider the user signs in with. The first, /api/session, returns information about the current user session. We’ll call this asynchronously from the front end in app/index.ts.

Open your server.js file for editing and navigate to the bottom. Just before the call to app.listen, insert the following:

 app.​get​(​'/api/session'​, (req, res) => {
 const​ session = {auth: req.isAuthenticated()};
  res.status(200).json(session);
 });

Passport adds an isAuthenticated method to the Express request object, req. Here, the /api/session route returns an object with an auth property that’ll be either true or false. After you save, you can try it out in a terminal with curl.

 $ ​​curl​​ ​​-s​​ ​​b4.example.com:60900/api/session
 {"auth":false}

Now, after the /auth/session route, add the following to set up the /auth/signout route.

 app.​get​(​'/auth/signout'​, (req, res) => {
  req.logout();
  res.redirect(​'/'​);
 });

Along with isAuthenticated, Passport adds a logout method to the req object. After calling it, we redirect the user back to the main page.

With these routes in place, we can now wire them into the front end.

Connecting the Session API to the Front End

Now that we have a /api/session route that can return a session object, let’s pass this object to the templates. This is necessary so we can conditionally show session-based info to the user (like the Sign Out link).

In your app directory, open the index.ts file for editing. To simplify requests to the back end, start by adding this utility function toward the top of the file:

 /**
  * Convenience method to fetch and decode JSON.
  */
 const​ fetchJSON = ​async​ (url, method = ​'GET'​) => {
 try​ {
 const​ response = ​await​ fetch(url, {method, credentials: ​'same-origin'​});
 return​ response.json();
  } ​catch​ (error) {
 return​ {error};
  }
 };

The fetchJSON async function takes two parameters: a required url string to fetch, and an optional method that defaults to GET. This function fetches the desired URL using the fetch API.[94]

Note the use of the credentials option. This ensures that credential information (cookies) is sent with the request. Without this option, by default fetch will not send cookies, meaning the back end would treat the request as unauthenticated.

If the fetch call succeeds, then we send back the Promise returned by response.json. Callers of fetchJSON will await the result and receive the deserialized JSON object. If anything went wrong, we return an object containing the error.

Now to use fetchJSON to get the session information. Scroll down in your index.ts file to the showView method. In the switch block, update the #welcome case to be this:

 case​ ​'#welcome'​:
 const​ session = ​await​ fetchJSON(​'/api/session'​);
  mainElement.innerHTML = templates.welcome({session});
 if​ (session.error) {
  showAlert(session.error);
  }
 break​;

Now, instead of calling the welcome template with an empty session object, we use fetchJSON to GET /api/session, then pass the resulting object to the template. If there was an error, we use showAlert to display it.

Scroll to the bottom of the index.ts file and find the anonymous async function that performs the page setup. Update it to read as follows:

 // Page setup.
 (​async​ () => {
 const​ session = ​await​ fetchJSON(​'/api/session'​);
  document.body.innerHTML = templates.main({session});
  window.addEventListener(​'hashchange'​, showView);
  showView().​catch​(err => window.location.hash = ​'#welcome'​);
 })();

Instead of calling main with an empty object, here we pass it the user’s session object.

It may seem odd to you that we’re reaching out to /api/session twice instead of doing it just once and reusing that object throughout. The reason is that the session may change over time.

Cookies do not last forever. Eventually they expire. By default, the cookies that the Express session stack creates last about a day. This means that if users revisit the welcome page a day later, they’ll be signed out.

The right thing to do in that case is show them the sign-in buttons so they can reauthenticate. Grabbing a fresh session object from /api/session inside of the showView method ensures they’re seeing session-appropriate content.

Once you save index.ts, nodemon will automatically restart your service. You won’t see any visible changes in the UI yet; for that we’ll need to implement at least one authentication provider and sign in with it. Let’s start with Facebook, then proceed to Twitter and Google.