Table of Contents for
Node.js Complete Reference Guide

Version ebook / Retour

Cover image for bash Cookbook, 2nd Edition Node.js Complete Reference Guide by Diogo Resende Published by Packt Publishing, 2018
  1. Node.js Complete Reference Guide
  2. Title Page
  3. Copyright and Credits
  4. Node.js Complete Reference Guide
  5. About Packt
  6. Why subscribe?
  7. Packt.com
  8. Contributors
  9. About the authors
  10. Packt is searching for authors like you
  11. Table of Contents
  12. Preface
  13. Who this book is for
  14. What this book covers
  15. To get the most out of this book
  16. Download the example code files
  17. Conventions used
  18. Get in touch
  19. Reviews
  20. About Node.js
  21. The capabilities of Node.js
  22. Server-side JavaScript
  23. Why should you use Node.js?
  24. Popularity
  25. JavaScript at all levels of the stack
  26. Leveraging Google's investment in V8
  27. Leaner, asynchronous, event-driven model
  28. Microservice architecture
  29. Node.js is stronger for having survived a major schism and hostile fork
  30. Threaded versus event-driven architecture
  31. Performance and utilization
  32. Is Node.js a cancerous scalability disaster?
  33. Server utilization, the business bottom line, and green web hosting
  34. Embracing advances in the JavaScript language
  35. Deploying ES2015/2016/2017/2018 JavaScript code
  36. Node.js, the microservice architecture, and easily testable systems
  37. Node.js and the Twelve-Factor app model
  38. Summary
  39. Setting up Node.js
  40. System requirements
  41. Installing Node.js using package managers
  42. Installing on macOS with MacPorts
  43. Installing on macOS with Homebrew
  44. Installing on Linux, *BSD, or Windows from package management systems
  45. Installing Node.js in the Windows Subsystem for Linux (WSL)
  46. Opening an administrator-privileged PowerShell on Windows
  47. Installing the Node.js distribution from nodejs.org
  48. Installing from source on POSIX-like systems
  49. Installing prerequisites
  50. Installing developer tools on macOS
  51. Installing from source for all POSIX-like systems
  52. Installing from source on Windows
  53. Installing multiple Node.js instances with nvm
  54. Installing nvm on Windows
  55. Native code modules and node-gyp
  56. Node.js versions policy and what to use
  57. Editors and debuggers
  58. Running and testing commands
  59. Node.js's command-line tools
  60. Running a simple script with Node.js
  61. Conversion to async functions and the Promise paradigm
  62. Launching a server with Node.js
  63. NPM – the Node.js package manager
  64. Node.js, ECMAScript 2015/2016/2017, and beyond 
  65. Using Babel to use experimental JavaScript features
  66. Summary
  67. Node.js Modules
  68. Defining a module
  69. CommonJS and ES2015 module formats
  70. CommonJS/Node.js module format
  71. ES6 module format
  72. JSON modules
  73. Supporting ES6 modules on older Node.js versions
  74. Demonstrating module-level encapsulation
  75. Finding and loading CommonJS and JSON modules using require
  76. File modules
  77. Modules baked into Node.js binary
  78. Directories as modules
  79. Module identifiers and pathnames
  80. An example of application directory structure
  81. Finding and loading ES6 modules using import
  82. Hybrid CommonJS/Node.js/ES6 module scenarios
  83. Dynamic imports with import()
  84. The import.meta feature
  85. npm - the Node.js package management system
  86. The npm package format
  87. Finding npm packages
  88. Other npm commands
  89. Installing an npm package
  90. Installing a package by version number
  91. Global package installs
  92. Avoiding global module installation
  93. Maintaining package dependencies with npm
  94. Automatically updating package.json dependencies
  95. Fixing bugs by updating package dependencies
  96. Packages that install commands
  97. Configuring the PATH variable to handle commands installed by modules
  98. Configuring the PATH variable on Windows
  99. Avoiding modifications to the PATH variable
  100. Updating outdated packages you've installed
  101. Installing packages from outside the npm repository
  102. Initializing a new npm package
  103. Declaring Node.js version compatibility
  104. Publishing an npm package
  105. Explicitly specifying package dependency version numbers
  106. The Yarn package management system
  107. Summary
  108. HTTP Servers and Clients
  109. Sending and receiving events with EventEmitters
  110. JavaScript classes and class inheritance
  111. The EventEmitter Class
  112. The EventEmitter theory
  113. HTTP server applications
  114. ES2015 multiline and template strings
  115. HTTP Sniffer – listening to the HTTP conversation
  116. Web application frameworks
  117. Getting started with Express
  118. Setting environment variables in Windows cmd.exe command line
  119. Walking through the default Express application
  120. The Express middleware
  121. Middleware and request paths
  122. Error handling
  123. Calculating the Fibonacci sequence with an Express application
  124. Computationally intensive code and the Node.js event loop
  125. Algorithmic refactoring
  126. Making HTTP Client requests
  127. Calling a REST backend service from an Express application
  128. Implementing a simple REST server with Express
  129. Refactoring the Fibonacci application for REST
  130. Some RESTful modules and frameworks
  131. Summary
  132. Your First Express Application
  133. Promises, async functions, and Express router functions
  134. Promises and error handling
  135. Flattening our asynchronous code
  136. Promises and generators birthed async functions
  137. Express and the MVC paradigm
  138. Creating the Notes application
  139. Your first Notes model
  140. Understanding ES-2015 class definitions
  141. Filling out the in-memory Notes model
  142. The Notes home page
  143. Adding a new note – create
  144. Viewing notes – read
  145. Editing an existing note – update
  146. Deleting notes – destroy
  147. Theming your Express application
  148. Scaling up – running multiple Notes instances
  149. Summary
  150. Implementing the Mobile-First Paradigm
  151. Problem – the Notes app isn't mobile friendly
  152. Mobile-first paradigm
  153. Using Twitter Bootstrap on the Notes application
  154. Setting it up
  155. Adding Bootstrap to application templates
  156. Alternative layout frameworks
  157. Flexbox and CSS Grids
  158. Mobile-first design for the Notes application
  159. Laying the Bootstrap grid foundation
  160. Responsive page structure for the Notes application
  161. Using icon libraries and improving visual appeal
  162. Responsive page header navigation bar
  163. Improving the Notes list on the front page
  164. Cleaning up the Note viewing experience
  165. Cleaning up the add/edit note form
  166. Cleaning up the delete-note window
  167. Building a customized Bootstrap
  168. Pre-built custom Bootstrap themes
  169. Summary
  170. Data Storage and Retrieval
  171. Data storage and asynchronous code
  172. Logging
  173. Request logging with Morgan
  174. Debugging messages
  175. Capturing stdout and stderr
  176. Uncaught exceptions
  177. Unhandled Promise rejections
  178. Using the ES6 module format
  179. Rewriting app.js as an ES6 module
  180. Rewriting bin/www as an ES6 module
  181. Rewriting models code as ES6 modules
  182. Rewriting router modules as ES6 modules
  183. Storing notes in the filesystem
  184. Dynamic import of ES6 modules
  185. Running the Notes application with filesystem storage
  186. Storing notes with the LevelUP data store
  187. Storing notes in SQL with SQLite3
  188. SQLite3 database schema
  189. SQLite3 model code
  190. Running Notes with SQLite3
  191. Storing notes the ORM way with Sequelize
  192. Sequelize model for the Notes application
  193. Configuring a Sequelize database connection
  194. Running the Notes application with Sequelize
  195. Storing notes in MongoDB
  196. MongoDB model for the Notes application
  197. Running the Notes application with MongoDB
  198. Summary
  199. Multiuser Authentication the Microservice Way
  200. Creating a user information microservice
  201. User information model
  202. A REST server for user information
  203. Scripts to test and administer the user authentication server
  204. Login support for the Notes application
  205. Accessing the user authentication REST API
  206. Login and logout routing functions
  207. Login/logout changes to app.js
  208. Login/logout changes in routes/index.mjs
  209. Login/logout changes required in routes/notes.mjs
  210. View template changes supporting login/logout
  211. Running the Notes application with user authentication
  212. Twitter login support for the Notes application
  213. Registering an application with Twitter
  214. Implementing TwitterStrategy
  215. Securely keeping secrets and passwords
  216. The Notes application stack
  217. Summary
  218. Dynamic Client/Server Interaction with Socket.IO
  219. Introducing Socket.IO
  220. Initializing Socket.IO with Express
  221. Real-time updates on the Notes homepage
  222. The Notes model as an EventEmitter class
  223. Real-time changes in the Notes home page
  224. Changing the homepage and layout templates
  225. Running Notes with real-time homepage updates
  226. Real-time action while viewing notes
  227. Changing the note view template for real-time action
  228. Running Notes with real-time updates while viewing a note
  229. Inter-user chat and commenting for Notes
  230. Data model for storing messages
  231. Adding messages to the Notes router
  232. Changing the note view template for messages
  233. Using a Modal window to compose messages
  234. Sending, displaying, and deleting messages
  235. Running Notes and passing messages
  236. Other applications of Modal windows
  237. Summary
  238. Deploying Node.js Applications
  239. Notes application architecture and deployment considerations
  240. Traditional Linux Node.js service deployment
  241. Prerequisite – provisioning the databases
  242. Installing Node.js on Ubuntu
  243. Setting up Notes and user authentication on the server
  244. Adjusting Twitter authentication to work on the server
  245. Setting up PM2 to manage Node.js processes
  246. Node.js microservice deployment with Docker
  247. Installing Docker on your laptop
  248. Starting Docker with Docker for Windows/macOS
  249. Kicking the tires of Docker
  250. Creating the AuthNet for the user authentication service
  251. MySQL container for Docker
  252. Initializing AuthNet
  253. Script execution on Windows
  254. Linking Docker containers
  255. The db-userauth container
  256. Dockerfile for the authentication service
  257. Configuring the authentication service for Docker
  258. Building and running the authentication service Docker container
  259. Exploring Authnet
  260. Creating FrontNet for the Notes application
  261. MySQL container for the Notes application
  262. Dockerizing the Notes application
  263. Controlling the location of MySQL data volumes
  264. Docker deployment of background services
  265. Deploying to the cloud with Docker compose
  266. Docker compose files
  267. Running the Notes application with Docker compose
  268. Deploying to cloud hosting with Docker compose
  269. Summary
  270. Unit Testing and Functional Testing
  271. Assert – the basis of testing methodologies
  272. Testing a Notes model
  273. Mocha and Chai­ – the chosen test tools
  274. Notes model test suite
  275. Configuring and running tests
  276. More tests for the Notes model
  277. Testing database models
  278. Using Docker to manage test infrastructure
  279. Docker Compose to orchestrate test infrastructure
  280. Executing tests under Docker Compose
  281. MongoDB setup under Docker and testing Notes against MongoDB
  282. Testing REST backend services
  283. Automating test results reporting
  284. Frontend headless browser testing with Puppeteer
  285. Setting up Puppeteer
  286. Improving testability in the Notes UI
  287. Puppeteer test script for Notes
  288. Running the login scenario
  289. The Add Note scenario
  290. Mitigating/preventing spurious test errors in Puppeteer scripts
  291. Configuring timeouts
  292. Tracing events on the Page and the Puppeteer instance
  293. Inserting pauses
  294. Avoiding WebSockets conflicts
  295. Taking screenshots
  296. Summary
  297. REST – What You Did Not Know
  298. REST fundamentals
  299. Principle 1 – Everything is a resource
  300. Principle 2 – Each resource is identifiable by a unique identifier
  301. Principle 3 – Manipulate resources via standard HTTP methods
  302. Principle 4 – Resources can have multiple representations
  303. Principle 5 – Communicate with resources in a stateless manner
  304. The REST goals
  305. Separation of the representation and the resource
  306. Visibility
  307. Reliability
  308. Scalability and performance
  309. Working with WADL
  310. Documenting RESTful APIs with Swagger
  311. Taking advantage of the existing infrastructure
  312. Summary
  313. Building a Typical Web API
  314. Specifying the API
  315. Implementing routes
  316. Querying the API using test data
  317. Content negotiation
  318. API versioning
  319. Self-test questions
  320. Summary
  321. Using NoSQL Databases
  322. MongoDB – a document store database
  323. Database modeling with Mongoose
  324. Testing a Mongoose model with Mocha
  325. Creating a user-defined model around a Mongoose model
  326. Wiring up a NoSQL database module to Express
  327. Self-test questions
  328. Summary
  329. Restful API Design Guidelines
  330. Endpoint URLs and HTTP status codes best practices
  331. Extensibility and versioning
  332. Linked data
  333. Summary
  334. Implementing a Full Fledged RESTful Service
  335. Working with arbitrary data
  336. Linking
  337. Implementing paging and filtering
  338. Caching
  339. Supplying the Cache-Control header in Express applications
  340. Discovering and exploring RESTful services
  341. Summary
  342. Consuming a RESTful API
  343. Consuming RESTful services with jQuery
  344. Troubleshooting and identifying problems on the wire
  345. Cross Origin Resource Sharing
  346. Content Delivery Networks
  347. Handling HTTP status codes on the client side
  348. Summary
  349. Securing the Application
  350. Authentication
  351. Basic authentication
  352. Passport
  353. Passport's basic authentication strategy
  354. Passport's OAuth Strategy
  355. Passport's third-party authentication strategies
  356. Authorization
  357. Transport layer security
  358. Self-test questions
  359. Summary
  360. The Age of Microservices
  361. From monolith to microservices
  362. Patterns of microservices
  363. Decomposable
  364. Autonomous
  365. Scalable
  366. Communicable
  367. Summary
  368. Modules and Toolkits
  369. Seneca
  370. Hydra
  371. Summary
  372. Building a Microservice
  373. Using Hydra
  374. Using Seneca
  375. Plugins
  376. Summary
  377. State
  378. State
  379. Storing state
  380. MySQL
  381. RethinkDB
  382. Redis
  383. Conclusion
  384. Security
  385. Summary
  386. Testing
  387. Integrating tests
  388. Using chai
  389. Adding code coverage
  390. Covering all code
  391. Mocking our services
  392. Summary
  393. Design Patterns
  394. Choosing patterns
  395. Architectural patterns
  396. Front Controller
  397. Layered
  398. Service Locator
  399. Observer
  400. Publish-Subscribe
  401. Using patterns
  402. Planning your microservice
  403. Obstacles when developing
  404. Summary
  405. Other Books You May Enjoy
  406. Leave a review - let other readers know what you think

Specifying the API

The very first thing a project usually starts with is a definition of the operations the API will expose. According to the REST principles, an operation is exposed by an HTTP method and a URI. The action performed by each operation should not contradict the natural meaning of its HTTP method. The following table specifies the operations of our API in detail:

Method URI Description
GET /category Retrieves all available categories in the catalog.
GET /category/{category-id}/ Retrieves all the items available under a specific category.
GET   /category/{category-id}/{item-id}  Retrieves an item by its ID under a specific category.
POST /category

Creates a new category; if it exists, it will update it.

POST  /category/{category-id}/
Creates a new item in a specified category. If the item exists, it will update it.
PUT   /category/{category-id}  Updates a category.
PUT /category/{category-id}/{item-id} Updates an item in a specified category.
DELETE /category/{category-id} Deletes an existing category.
DELETE /category/{category-id}/{item-id} Deletes an item in a specified category.

 

The second step is to choose an appropriate format for our catalog application's data. JSON objects are natively supported by JavaScript. They are easy to extend during the evolution of an application and are consumable by almost any platform available. Thus, the JSON format seems to be our logical choice for us. Here is the JSON representation of an item, and category objects that will be used throughout this book:

{ 
    "itemId": "item-identifier-1", 
    "itemName": "Sports Watch", 
    "category": "Watches", 
"categoryId": 1, "price": 150,
"currency": "EUR" }

{
"categoryName" : "Watches",
"categoryId" : "1",
"itemsCount" : 100,
"items" : [{
"itemId" : "item-identifier-1",
"itemName":"Sports Watch",
"price": 150,
"currency" : "EUR"
}]
}

So far, our API has defined a set of operations and the data format to be used. The next step is to implement a  module that will export functions serving each of the operations in the route.

To begin with, let's create a new Node.js Express project. Select a directory where your projects will be stored and from your shell Terminal, execute express chapter3. If you are using Windows, you will need to install the express-generator module before generating the project. The express-generator will create your an initial express project layout in the selected directory. This layout provides the default project structure for you, ensuring that your Express project follows the standard project structure. It makes your project easier to navigate.

The next step is to import the project into the Atom IDE. Right-click anywhere in the Projects tab and select Add project folder then select the directory Express generated for you.

As you can see, Express has done some background work for us and has created a starting point for our application: app.js. It has also created the package.json file for us. Let's take a look at each of these files, starting with package.json:

{
"name": "chapter3",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "test"
},
"author": "",
"license": "ISC",
"dependencies": {
"dependencies": {
"body-parser": "~1.13.2",
"cookie-parser": "~1.3.5",
"debug": "~2.2.0",
"express": "~4.16.1",
"jade": "~1.11.0",
"morgan": "~1.6.1",
"serve-favicon": "~2.3.0"

}
}

As we created a blank Node.js Express project, we initially have dependencies only to the Express framework, some middleware modules such as morgan, body-parser, and cookie-parser, and the Jade template language. Jade is a straightforward template language used to produce HTML code inside templates. If you are interested in it, you can find out more about it at http://www.jade-lang.com.

The current version of the Express framework at the time of writing is 4.16.1; to update it, execute npm install express@4.16.1 --save from the chapter3 directory. This command will update the dependency of the application to the desired version. The --save option will update and save the new version of the dependency in the project's package.json file.

When you introduce new module dependencies, it is up to you to keep the package.json file up to date in order to maintain an accurate state of the modules your application depends on. 

We will come to what middleware modules are a bit later in the chapter.

For now, we will ignore the content of the public and view directories as it is not relevant to our RESTful service. They contain the auto-generated stylesheets and template files that might be helpful, if we decide to develop a web-based consumer of the services at a later stage.

We've already mentioned that the Express project created a starting point for our web application in app.js. Let's take a deeper look at it:

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
var users = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});


module.exports = app;

Obviously, the Express generator has done a lot for us as it has instantiated the Express framework and has assigned a complete development environment around it. It has done the following:

  • Configured the middleware to be used in our application, body-parser, the default router, as well as error handler middleware for our development environment
  • Injected a logger instance of the morgan middleware module
  • Configured the Jade template, as it has been selected as the default template for our application
  • Configured the default URI that our Express application will be listening to, / and /users, and created dummy handle functions for them

You will have to install all the modules used in app.js in order to start the generated application successfully. Also, make sure you update the dependencies of your package.json file using the --save option after installing them.

The Express generator also created a starting script for the application. It is under the  bin/www directory of your project and looks like the following snippet:

#!/usr/bin/env node

/**
* Module dependencies.
*/

var app = require('../app');
var debug = require('debug')('chapter3:server');
var http = require('http');

/**
* Get port from environment and store in Express.
*/

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
* Create HTTP server.
*/

var server = http.createServer(app);

/**
* Listen on provided port, on all network interfaces.
*/

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

/**
* Normalize a port into a number, string, or false.
*/

function normalizePort(val) {
var port = parseInt(val, 10);

if (isNaN(port)) {
// named pipe
return val;
}

if (port >= 0) {
// port number
return port;
}

return false;
}

/**
* Event listener for HTTP server "error" event.
*/

function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}

var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;

// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}

/**
* Event listener for HTTP server "listening" event.
*/

function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}

To start the application, execute node bin/www; this will execute the script above and will start the Node.js application. So requesting http://localhost:3000 in your browser will result in calling the default GET handler, which gives a warm welcome response:

Default welcome message from an Express application

The generator created a dummy routes/users.js; it exposes a route linked to a dummy module available at the /users location. Requesting it will result in calling the list function of the user's route, which outputs a static response: respond with a resource.

Our application will not be using a template language and style sheets, so let's get rid of the lines that set the views and view engine properties in the application configuration. In addition, we will be implementing our own routes. Thus, we don't need the binding of the / and /users URIs for our app, neither do we need the user module; instead, we will utilize a catalog module and from a route:

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
var catalog = require('./routes/catalog')
var app = express();

//uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/catalog', catalog);


// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});

//development error handler will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}

// production error handler no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});

module.exports = app;

So after this cleanup, our application looks a lot cleaner and we are ready to move forward.

Here are the basic rules that apply to the middleware chain:

  • A middleware function has the following signature: function (request, response, next).
  • Middleware functions are executed in the exact order in which they have been added to the application chain. This means that if you want your middleware function to be called before a specific route, you need to add it before declaring the route.
  • Middleware functions use their third parameter, next, as a function to indicate that they have completed their work and to exit. When the next() parameter of the last function in the chain has been called, the chained execution is completed and the request and the response objects reach the defined handlers, in the state set by the middleware.

Now that we know what a middleware function is, let's clarify what the currently used middleware functions provide our application with. The body-parser middleware is the Express framework built in a parser. It parses the request body and populates the request object after the middleware execution finishes, that is, it provides JSON payload handling.

Now it is time to move on and implement our user module that will be mapped to our URIs. The module will be named modules/catalog.js:

var fs = require('fs');

function readCatalogSync() {
var file = './data/catalog.json';
if (fs.existsSync(file)) {
var content = fs.readFileSync(file);
var catalog = JSON.parse(content);
return catalog;
}
return undefined;
}

exports.findItems = function(categoryId) {
console.log('Returning all items for categoryId: ' + categoryId);
var catalog = readCatalogSync();
if (catalog) {
var items = [];
for (var index in catalog.catalog) {
if (catalog.catalog[index].categoryId === categoryId) {
var category = catalog.catalog[index];
for (var itemIndex in category.items) {
items.push(category.items[itemIndex]);
}
}
}
return items;
}
return undefined;
}

exports.findItem = function(categoryId, itemId) {
console.log('Looking for item with id' + itemId);
var catalog = readCatalogSync();
if (catalog) {
for (var index in catalog.catalog) {
if (catalog.catalog[index].categoryId === categoryId) {
var category = catalog.catalog[index];
for (var itemIndex in category.items) {
if (category.items[itemIndex].itemId === itemId) {
return category.items[itemIndex];
}
}
}
}
}
return undefined;
}

exports.findCategoryies = function() {
console.log('Returning all categories');
var catalog = readCatalogSync();
if (catalog) {
var categories = [];
for (var index in catalog.catalog) {
var category = {};
category["categoryId"] = catalog.catalog[index].categoryId;
category["categoryName"] = catalog.catalog[index].categoryName;

categories.push(category);
}
return categories;
}
return [];
}

The catalog module is built around the catalog.json file, stored in the data directory. The content of the source file is read synchronously using the File System module, fs, within the readCatalogSync function. The File System module provides multiple useful filesystem operations such as functions for creating, renaming, or deleting files or directories; truncating; linking; chmod functions; as well as synchronous and asynchronous file access for reading and writing data. In our sample application, we aim to use the most straightforward approach, so we implement functions that read the catalog.json file by utilizing the readFileSync function of the File System module. It returns the content of a file as a string, within a synchronous call. All other functions of the module are exported and can be used to query the content of the source file, based on different criteria.

The catalog module exports the following functions:

  • findCategories: This returns an array of JSON objects containing all the categories in the catalog.json file
  • findItems (categoryId): This returns an array JSON objects representing all the items in a given category
  • findItem(categoryId, itemId): This returns a JSON object representing a single item in a given category

Now that we have three complete functions, let's see how to bind them to our Express application.