
To all the full-stack developers of the world.
Pro MERN Stack is for full-stack developers, architects, and team leads wanting to learn about this stack built with Mongo, Express, React, and Node.
It was in late December 2016 that I finished the manuscript of the first edition. But within just a few months after the book was out, it was outdated. React Router released a new version, 3.0, and this alone was enough to warrant a new edition. I tried to communicate to readers via GitHub issues, asking them to use the older version of React Router. It worked for a while, but this was less than satisfactory.
That was not all. Within another few months, there was a new Node.js LTS version, then React 16, and then a new version of MongoDB, and then Babel. Almost every module used in the MERN stack was upgraded and soon Pro MERN Stack became outdated. This pace of change amazed me. Although every new change was great, it did no good for technical authors like me.
I did wait for things to stabilize a bit before initiating the second edition of Pro MERN Stack. And I am glad I did, because I believe the current set of tools that form the MERN stack have reached a good level of maturity and therefore will be reasonably stable for some time to come.
Compared to the first edition, there are the expected newer versions of all the tools and libraries. But importantly, I have introduced more modern ways of doing things.
I have replaced REST APIs with GraphQL, which I believe is a superior way of implementing APIs. There is a new architecture that separates the user interface and the APIs into two different servers. Instead of using in-memory sessions to track logged-in users, I used JSON Web Tokens. I have simplified server rendering by using a global store instead of the deprecated React Context. Finally, I introduced a new chapter on deployment using Heroku.
I also changed many things that one needs to do as an author to enhance the readers’ experiences. I added illustrations and more explanations to code snippets and increased the granularity of the sections. All these changes, in my opinion, will make Pro MERN Stack , Second Edition, far superior to the first edition. It’s not just an up-to-date version of the first edition, but it enhances the learning experience significantly.
This book would not have been possible without the official reviewers. But the most I owe to is my wife, Keerthana, for the illustrations. Not just for drawing them, but for designing them as well. And to do that, she had to read each chapter, understand it in detail, and try out the code in it. Honestly, this book would not have been possible without her.

has experience with all kinds of programming, from 8-bit, hand-assembled code on an 8085 to AWS Lambda. He not only loves to solve problems using software, but he also looks for the right mix of technology and processes to make a software product team most efficient. He learned software development at companies such as Corel, Wipro, and Barracuda Networks, not just as a programmer but also as a leader of teams at those companies.
Vasan studied at IIT Madras and IIM Bangalore. In his current job as CTO at Accel, he mentors startups on all things tech. His Twitter handle is @vasansr. While not mentoring or coding (or writing books!), Vasan runs half marathons and plays five-a-side soccer.

is a full-stack developer at Wipro Digital, delivering solutions using various upcoming and existing technologies like MERN stack, GraphQL, and micro-services. He currently lives in London and loves traveling. In his leisure time, he watches films and directs short movies.
Web application development is not what it used to be, even a few years back. Today, there are so many options, and the uninitiated are often confused about what’s good for them. There are many choices; not just the broad stack (the various tiers or technologies used), but also for tools that aid in development. This book stakes a claim that the MERN stack is great for developing a complete web application and takes the reader through all that is necessary to get that done.
In this chapter, I’ll give a broad overview of the technologies that the MERN stack consists of. I won’t go into details or examples in this chapter, instead, I’ll just introduce the high-level concepts. I’ll focus on how these concepts affect an evaluation of whether MERN is a good choice for your next web application project.
Any web application is built using multiple technologies. The combinations of these technologies is called a “stack ,” popularized by the LAMP stack, which is an acronym for Linux, Apache, MySQL, PHP, which are all open-source software. As web development matured and their interactivity came to the fore, Single Page Applications (SPAs) became more popular. An SPA is a web application paradigm that avoids fetching the contents of an entire web page from the server to display new contents. It instead uses lightweight calls to the server to get some data or snippets and changes the web page. The result looks quite nifty as compared to the old way of reloading the page entirely. This brought about a rise in front-end frameworks, since a lot of the work was done on the front-end. At approximately the same time, though completely unrelated, NoSQL databases also started gaining popularity.
The MEAN (MongoDB, Express, AngularJS, Node.js) stack was one of the early open-source stacks that epitomized this shift toward SPAs and adoption of NoSQL. AngularJS, a front-end framework based on the Model View Controller (MVC) design pattern, anchored this stack. MongoDB, a very popular NoSQL database, was used for persistent data storage. Node.js, a server-side JavaScript runtime environment, and Express, a web-server built on Node.js, formed the middle-tier, or the web server. This stack was arguably the most popular stack for any new web application until a few years back.
Not exactly competing, but React, an alternate front-end technology created by Facebook, has been gaining popularity and offers an alternative to AngularJS. It thus replaces the “A” with an “R” in MEAN, to give us the MERN Stack. I said “not exactly” since React is not a full-fledged MVC framework. It is a JavaScript library for building user interfaces, so in some sense, it’s the View part of the MVC.
Although we pick a few defining technologies to define a stack, these are not enough to build a complete web application. Other tools are required to help the process of development, and many libraries are needed to complement React. This book is about building a complete web application based on the MERN stack and all these related tools and libraries.
Developers and architects who have prior experience in any web app stack other than the MERN stack will find the book useful for learning about this modern stack. Prior knowledge of how web applications work is required. Knowledge of JavaScript is also required. It is further assumed that the reader knows the basics of HTML and CSS. It will greatly help if you are also familiar with the version control tool git; you could try out the code just by cloning the git repository that holds all the source code described in this book and running each step by checking out a branch.
The code in the book uses the latest features of JavaScript (ES2015+), and it is assumed that you well-versed in these features such as classes, fat-arrow functions, const keyword, etc. Whenever I first use any of these modern JavaScript features, I will point it out using a note so that you are aware that it is a new feature. In case you are not familiar with a particular feature, you can read up on it as and when you encounter it.
If you have decided that your new app will use the MERN stack, then this book is a perfect enabler for you that lets you quickly get off the ground. Even if you have not, reading the book will get you excited about MERN and equip you with enough knowledge to make a choice for a future project. The most important thing you will learn is to put together multiple technologies and build a complete, functional web application, and you can be called a full-stack developer or architect on MERN.
Although the focus of the book is to let you learn how to build a complete web application, most of the book revolves around React. That’s just because, as is true of most modern SPAs, the front-end code forms the bulk. And in this case, React is used for the front-end.
The tone of the book is tutorial-like and designed for learning by doing. We will build a web application during the course of the book. I use the term “we” because you will need to write code just as I show you the code that is to be written as part of the plentiful code listings. Unless you write the code yourself alongside me and solve the exercises, you will not get the full benefit of the book. I encourage you not to copy-paste; instead please type out the code. I find this very valuable in the learning process. There are very small nuances—e.g., types of quotes—that can cause a big difference. When you type out the code, you are much more conscious of this than when you are just reading it.
Sometimes, you may run into situations where what you typed in doesn’t work. In such cases, you may want to copy-paste to ensure that the code is correct and overcomes any typos you may have made. In such cases, do not copy-paste from the electronic version of the book, as the typesetting may not be faithful to the actual code. I have created a GitHub repository at https://github.com/vasansr/pro-mern-stack-2 for you to compare, and in unavoidable circumstances, to copy-paste from.
I have also added a checkpoint (a git branch in fact) after every change that can be tested in isolation so that you can look at the exact diffs between two checkpoints, online. The checkpoints and links to the diffs are listed in the home page (the README) of the repository. You may find this more useful than looking at the entire source, or even the listings in the text of this book, as GitHub diffs are far more expressive than what I can show in print.
Rather than cover one topic or technology per section, I have adopted a more practical and problem-solving approach. We will have developed a full-fledged working application by the end of the book, but we’ll start small with a Hello World example. Just as in a real project, we will add more features to the application as we progress. When we do this, we’ll encounter tasks that need additional concepts or knowledge to proceed. For each of these, I will introduce the concept or technology that can be used, and I’ll discuss that in detail.
Thus, you may not find every chapter or section devoted purely to one topic or technology. Some chapters may focus on a technology and others may address a set of goals we want to achieve in the application. We will be switching between technologies and tools as we progress.
I have included exercises wherever possible, which makes you either think or look up various resources on the Internet. This is so that you know where to get additional information for things that are not covered in the book, typically very advanced topics or APIs.
I have chosen an issue-tracking application as the application that we’ll build together. It’s something most developers can relate to, at the same time has many of the attributes and requirements that any enterprise application will have, commonly referred to as a “CRUD” application (CRUD stands for Create, Read, Update, Delete of a database record).
Many of the conventions used in the book quite obvious, so I’ll not explain all of them. I’ll only cover some conventions with respect to how the sections are structured and how changes to the code are shown because it’s not very obvious.
Each chapter has multiple sections, and each section is devoted to one set of code changes that results in a working application that can be run and tested. A section can have multiple listings, but each of them may not be testable by itself. Every section will also have a corresponding entry in the GitHub repository, where you can see the complete source of the application as of the end of that section, as well as the differences between the previous and the current section. You will find the difference view very useful to identify the changes made in the section.
All code changes will appear in listings within the section, but please do not rely on their accuracy. The reliable and working code can be found in the GitHub repository, which could even have undergone last-minute changes that couldn’t make it to the printed book in time. All listings will have a listing caption, which will include the name of the file being changed or created.
You may use the GitHub repository to report problems in the printed book. But before you do that, do check the existing list of issues to see if anyone else has reported the same. I will monitor the issues and post resolutions and, if necessary, correct the code in the GitHub repository.
A listing is a full listing if it contains a file, a class, a function, or an object in its entirety. A full listing may also contain two or more classes, functions, or objects, but not multiple files. In such a case, if the entities are not consecutive, I’ll use ellipses to indicate chunks of unchanged code.
All commands used in the book can also be found in the GitHub repository in a file called commands.md. This is so that errors in the book can be corrected after the book has been published, and also to be a more reliable source for copy-pasting. Again, you are encouraged not to copy-paste these commands, but if you are forced to because you find something is not working, then please copy-paste from the GitHub repository rather than the book’s text.
These commands are also collected in one file, called mongoCommands.md in the GitHub repository.
You will need a computer that can run your server and do other tasks such as compilation. You will also need a browser to test your application. I recommend a Linux-based computer such as Ubuntu, or a Mac as your development server, but with minor changes, you could also use a Windows PC.
Running Node.js directly on Windows will also work, but the code samples in this book assume a Linux-based PC or Mac. If you choose to run directly on a Windows PC, you may have to make appropriate changes, especially when running commands in the shell, using a copy instead of using soft links, and in rare cases, to deal with \ vs. / in path separators.
One option would be to try to run an Ubuntu server Virtual Machine (VM) using Vagrant ( https://www.vagrantup.com/ ). This is helpful because you will eventually deploy your code on a Linux-based server, and it is best to get used to that environment from the beginning. But you may find it difficult to edit files, because in an Ubuntu server, you only have a console. An Ubuntu desktop VM may work better for you, but it will need more memory.
Further, to keep the book concise, I have not included installation instructions for packages, and they are different for different operating systems. You will need to follow the installation instructions from the package providers’ websites. And in many cases, I have not included direct links to websites and I ask you to look them up. This is due to a couple of reasons. The first is to let you learn by yourself how to search for these. The second is that any link I provide may have moved to another location due to the fast-paced changes that the MERN stack was experiencing at the time of writing this book.
I’ll give a quick introduction to the main components that form the MERN stack, and a few other libraries and tools that we’ll be using to build our web application. I’ll just touch upon the salient features and leave the details to other chapters where they are more appropriate.
React anchors the MERN stack. In some sense, this is the defining component of the MERN stack.
React is an open-source JavaScript library maintained by Facebook that can be used for creating views rendered in HTML. Unlike AngularJS, React is not a framework. It is a library. Thus, it does not, by itself, dictate a framework pattern such as the MVC pattern. You use React to render a view (the V in MVC), but how to tie the rest of the application together is completely up to you.
Not just Facebook itself, but there are many other companies that use React in production like Airbnb, Atlassian, Bitbucket, Disqus, Walmart, etc. The 120,000 stars on its GitHub repository is an indication of its popularity.
I’ll discuss a few things about React that make it stand out.
The Facebook folks built React for their own use, and later they made it open-source. Now, why did they have to build a new library when there are tons of them out there?
React was born not in the Facebook application that we all see, but in Facebook’s Ads organization. Originally, they used a typical client-side MVC model to start with, which had all the regular two-way data binding and templates. Views would listen to changes on models, and they would respond to those changes by updating themselves.
Soon, this got pretty hairy as the application became more and more complex. What would happen was that a change would cause an update, that would cause another update (because something changed due to that update), which would cause yet another, and so on. Such cascading updates became difficult to maintain, because there would be subtle differences in the code to update the view, depending on the root cause of the update.
Then they thought, why do we need to deal with all this, when all the code to depict the model in a view is already there? Aren’t we replicating the code by adding smaller and smaller snippets to manage transitions? Why can’t we use the templates (that is, the views) themselves to manage state changes?
That’s when they started thinking of building something that’s declarative rather than imperative.
React views are declarative. What this really means is that you, as a programmer, don’t have to worry about managing the effect of changes in the view’s state or the data. In other words, you don’t worry about transitions or mutations in the DOM caused by changes to the view’s state. Being declarative makes the views consistent, predictable, easier to maintain, and simpler to understand. It’s someone else’s problem to deal with transitions.
How does this work? Let’s compare how things work in React and how things work in the conventional approach, say, using jQuery.
A React component declares how the view looks, given the data. When the data changes, if you are used to the jQuery way of doing things, you’d typically do some DOM manipulation. If, for example, a new row has been inserted in a table, you’d create that DOM element and insert it using jQuery. But not in React. You just don’t do anything! The React library figures out how the new view looks and renders that.
Won’t this be too slow? Will it not cause the entire screen to be refreshed on every data change? Well, React takes care of this using its Virtual DOM technology. You declare how the view looks and React builds a virtual representation, an in-memory data structure, out of it. I’ll discuss more about this in Chapter 2, but for now, just think of the Virtual DOM as an intermediate representation, somewhere between an HTML and the actual DOM.
When things change, React builds a new virtual DOM based on the new truth (state) and compares it with the old (before things changed) virtual DOM. React then computes the differences in the old and the changed Virtual DOM, then applies these changes to the actual DOM.
Compared to manual updates as you would have performed in the jQuery way of doing things, this adds very little overhead because the algorithm to compute the differences in the Virtual DOM has been optimized to the hilt. Thus, we get the best of both worlds: not having to worry about implementing transitions and also the performance of minimal changes.
The fundamental building block of React is a component that maintains its own state and renders itself.
In React, all you do is build components. Then, you put components together to make another component that depicts a complete view or page. A component encapsulates the state of data and the view, or how it is rendered. This makes writing and reasoning about the entire application easier, by splitting it into components and focusing on one thing at a time.
Components talk to each other by sharing state information in the form of read-only properties to their child components and by callbacks to their parent components. I’ll dig deeper into this concept in a later chapter, but the gist of it is that components in React are very cohesive, but the coupling with one another is minimal.
Many web application frameworks rely on templates to automate the task of creating repetitive HTML or DOM elements. The templating language in these frameworks is something that the developer will have to learn and practice. Not in React.
React uses a full-featured programming language to construct repetitive or conditional DOM elements. That language is none other than JavaScript. For example, when you want to construct a table, you’d write a for(...) loop in JavaScript or use the map() function of Array.
There is an intermediate language to represent a Virtual DOM, and that is JSX (short for JavaScript XML), which is very much like HTML. This lets you create nested DOM elements in a familiar language rather than hand-construct them using JavaScript functions. Note that JSX is not a programming language; it is a representational markup like HTML. It’s also very similar to HTML so you don’t have to learn too much. More about this later.
In fact, you don’t have to use JSX—you can write pure JavaScript to create your virtual DOM if you prefer. But if you are used to HTML, it’s simpler to just use JSX. But don’t worry about it; it’s really not a new language that you’ll need to learn.
React can be run on the server too. That’s what isomorphic means: the same code can run on the server and the browser. This allows you to create pages on the server when required, for example, for SEO purposes. I’ll talk discuss how this works in a bit more detail later, in Chapter 12, which deals with server-side rendering. But to be able to run React code on the server, we do need something that can run JavaScript, and this is where I introduce Node.js.
Simply put, Node.js is JavaScript outside of a browser. The creators of Node.js just took Chrome’s V8 JavaScript engine and made it run independently as a JavaScript runtime. If you are familiar with the Java runtime that runs Java programs, you can easily relate to the JavaScript runtime: the Node.js runtime runs JavaScript programs.
Although you may find people who don’t think Node.js is fit for production use, claiming it was meant for the browser, there are many industry leaders who have chosen Node.js. Netflix, Uber, and LinkedIn are a few companies that use Node.js in production, and that should lend credibility as a robust and scalable environment to run the back-end of any application.
In a browser, you can load multiple JavaScript files, but you need an HTML page to do all that. You cannot refer another JavaScript file from one JavaScript file. But for Node.js, there is no HTML page that starts it all. In the absence of the enclosing HTML page, Node.js uses its own module system based on CommonJS to put together multiple JavaScript files.
Modules are like libraries. You can include the functionality of another JavaScript file (provided it’s written to follow a module’s specifications) by using the require keyword (which you won’t find in a browser’s JavaScript). You can therefore split your code into files or modules for the sake of better organization and load them using require. I’ll talk about the exact syntax in a later chapter; at this point it’s enough to note that compared to JavaScript on the browser, there is a cleaner way to modularize your code using Node.js.
Node.js ships with a bunch of core modules compiled into the binary. These modules provide access to the operating system elements such as the file system, networking, input/output, etc. They also provide some utility functions that are commonly required by most programs.
Apart from your own files and the core modules, you can also find a great amount of third-party open-source libraries available for easy installation. That brings us to npm.
npm is the default package manager for Node.js. You can use npm to install third-party libraries (packages) and manage dependencies between them. The npm registry ( www.npmjs.com ) is a public repository of all modules published by people for the purpose of sharing.
Although npm started off as a repository for Node.js modules, it quickly transformed into a package manager for delivering other JavaScript based modules, notably, those that can be used in the browser. jQuery, by far the most popular client-side JavaScript library, is available as an npm module. In fact, even though React is largely client-side code and can be included directly in your HTML as a script file, it is recommended instead that React is installed via npm. But once it’s installed as a package, we need something to put all the code together that can be included in the HTML so that the browser can get access to the code. For this, there are build tools such as Browserify or Webpack that can put together your own modules as well as third-party libraries in a bundle that can be included in the HTML.

Number of modules in various languages (source: www.modulecounts.com )
npm is not just easy to use both for creating and using modules; it also has a unique conflict resolution technique that allows multiple conflicting versions of a module to exist side-by-side to satisfy dependencies. Thus, in most cases, npm just works.
Node.js has an asynchronous, event driven, non-blocking input/output (I/O) model, as opposed to using threads to achieve multi-tasking.
Most other languages depend on threads to do things simultaneously. But in fact, there is no such thing as simultaneous when it comes to a single processor running your code. Threads give the feeling of simultaneousness by letting other pieces of code run while one piece waits (blocks) for some event to complete. Typically, these are I/O events such as reading from a file or communicating over the network. For example, on one line, you make a call to open a file, and on the next line, you have your file handle ready. What really happens is that your code is blocked (doing nothing) while the file is being opened. If you have another thread running, the operating system or the language will switch out this code and start running some other code during the blocked period.
Node.js, on the other hand, has no threads. It relies on callbacks to let you know that a pending task is completed. So, if you write a line of code to open a file, you supply it with a callback function to receive the results—the file handle. On the next line, you continue to do other things that don’t require the file handle. If you are used to asynchronous Ajax calls, you will immediately know what I mean. Event driven programming is natural to Node.js due to the underlying language constructs of JavaScript such as closures.
Node.js achieves multi-tasking using an event loop. This is nothing but a queue of events that need to be processed, and callbacks to be run on those events. In the previous example, the file being ready to be read will be an event that will trigger the callback you supplied while opening it. If you don’t understand this completely, don’t worry. The examples in the rest of this book should make you comfortable about how it really works.
On the one hand, an event-based approach makes Node.js applications fast and lets the programmer be blissfully oblivious of semaphores and locks that are utilized to synchronize multi-threaded events. On the other hand, writing code that is inherently asynchronous takes some learning and practice.
Node.js is just a runtime environment that can run JavaScript. To write a full-fledged web server by hand on Node.js directly is not easy, neither is it necessary. Express is a framework that simplifies the task of writing the server code.
The Express framework lets you define routes, specifications of what to do when an HTTP request matching a certain pattern arrives. The matching specification is regular expression (regex) based and is very flexible, like most other web application frameworks. The what-to-do part is just a function that is given the parsed HTTP request.
Express parses the request URL, headers, and parameters for you. On the response side, it has, as expected, all functionality required by web applications. This includes determining response codes, setting cookies, sending custom headers, etc. Further, you can write Express middleware, custom pieces of code that can be inserted in any request/response processing path to achieve common functionality such as logging, authentication, etc.
Express does not have a template engine built in, but it supports any template engine of your choice such as pug, mustache, etc. But, for an SPA, you will not need to use a server-side template engine. This is because all dynamic content generation is done on the client, and the web server only serves static files and data via API calls.
In summary, Express is a web server framework meant for Node.js. It’s not very different from many other web server frameworks in terms of what you can achieve with it.
MongoDB is the database used in the MERN stack. It is a NoSQL document-oriented database, with a flexible schema and a JSON-based query language. Not only do many modern companies (including Facebook and Google) use MongoDB in production, but some older established companies such as SAP and Royal Bank of Scotland have adopted MongoDB.
I’ll discuss a few things that MongoDB is (and is not) here.
NoSQL stands for “non-relational,” no matter what the acronym expands to. It’s essentially not a conventional database where you have tables with columns and rows and strict relationships among them. I find that there are two attributes of NoSQL databases that differentiate them from the conventional.
The first is their ability to horizontally scale by distributing the load over multiple servers. They do this by sacrificing an important (for some) aspect of the traditional databases: strong consistency. That is, the data is not necessarily consistent for very brief amounts of time across replicas. For more information, read up on the “CAP theorem” ( https://en.wikipedia.org/wiki/CAP_theorem ). But in reality, very few applications require web-scale, and this aspect of NoSQL databases comes into play very rarely.
The second, and according to me more important, aspect is that NoSQL databases are not necessarily relational databases. You don’t have to think of your objects in terms of rows and columns of tables. The difference between the representation in the application (objects) and on disk (rows in tables) is sometimes called impedance mismatch. This is a term borrowed from electrical engineering, which means, roughly, that we’re not talking the same language. Because of the impedance mismatch, we have to use a layer that translates or maps between objects and relations. These layers are called Object Relational Mapping (ORM) layers.
In MongoDB, instead, you can think of the persisted data just as you see them in your application code, that is, as objects or documents. This helps you avoid the ORM layer and think of the persisted data as naturally as you would in the application’s memory.
Compared to relational databases where data is stored in the form of relations, or tables, MongoDB is a document-oriented database. The unit of storage (comparable to a row) is a document, or an object, and multiple documents are stored in collections (comparable to a table). Every document in a collection has a unique identifier, using which it can be accessed. The identifier is indexed automatically.
Imagine the storage structure of an invoice, with the customer name, address, etc. and a list of items (lines) in the invoice. If you had to store this in a relational database, you would use two tables, say, invoice and invoice_lines, with the lines or items referring to the invoice via a foreign-key relation. Not so in MongoDB. You would store the entire invoice as a single document, fetch it, and update it in an atomic operation. This applies not just to line items in an invoice. The document can be any kind of deeply nested object.
Modern relational databases have started supporting one level of nesting by allowing array fields and JSON fields, but it is not the same as a true document database. MongoDB has the ability to index on deeply nested fields, which relational databases cannot do.
The downside is that the data is stored de-normalized. This means that data is sometimes duplicated, requiring more storage space. Also, things like renaming a master (catalog) entry name would mean sweeping through the database and updating all occurrences of the duplicated data. But then, storage has become relatively cheap these days, and renaming master entries are rare operations.
Storing an object in a MongoDB database does not have to follow a prescribed schema. All documents in a collection need not have the same set of fields.
This means that, especially during early stages of development, you don’t need to add/rename columns in the schema. You can quickly add fields in your application code without having to worry about database migration scripts. At first this may seem a boon, but in effect all it does is transfers the responsibility of data sanity from the database to your application code. I find that in larger teams and more stable products, it is better to have a strict or semi-strict schema. Using Object Document Mapping libraries such as mongoose (not covered in this book) alleviates this problem.
MongoDB’s language is JavaScript.
For relational databases, we had a query language called SQL. For MongoDB, the query language is based on JSON. You create, search for, make changes, and delete documents by specifying the operation in a JSON object. The query language is not English-like (you don’t SELECT or say WHERE), and therefore much easier to construct programmatically.
Data is also interchanged in JSON format. In fact, the data is natively stored in a variation of JSON called BSON (where B stands for Binary) in order to efficiently utilize space. When you retrieve a document from a collection, it is returned as a JSON object.
MongoDB comes with a shell that’s built on top of a JavaScript runtime like Node.js. This means that you have a powerful and familiar scripting language (JavaScript) to interact with the database via command line. You can also write code snippets in JavaScript that can be saved and run on the server (equivalent of stored procedures).
It’s hard to build any web application without using tools to help you on your way. Here’s a brief introduction to the other tools apart from the MERN stack components that we will be using to develop the sample application in this book.
React gives us only the View rendering capability and helps manage interactions in a single component. When it comes to transitioning between different views of the component and keeping the browser URL in sync with the current state of the view, we need something more.
This capability of managing URLs and history is called routing. It is similar to server-side routing that Express does: a URL is parsed and based on its components, a piece of code is associated with the URL. React-Router not only does this, but also manages the browser’s Back button functionality so that we can transition between what seem as pages without loading the entire page from the server. We could have built this ourselves, but React-Router is a very easy-to-use library that manages this for us.
Bootstrap, the most popular CSS framework, has been adapted to React and the project is called React-Bootstrap . This library not only gives us most of the Bootstrap functionality, but the components and widgets provided by this library also give us a wealth of information on how to design our own widgets and components.
There are other component/CSS libraries built for React (such as Material-UI, MUI, Elemental UI, etc.) and also individual components (such as react-select, react-treeview, and react-date-picker). All these are good choices too, depending on what you are trying to achieve. But I have found that React-Bootstrap is the most comprehensive single library with the familiarity of Bootstrap (which I presume most of you know already).
This tool is indispensable when it comes to modularizing code. There are other competing tools such as Bower and Browserify which also serve the purpose of modularizing and bundling all the client code, but I found that Webpack is easier to use and does not require another tool (like gulp or grunt) to manage the build process.
We will be using Webpack not just to modularize and build the client-side code into a bundle to deliver to the browser, but also to “compile” some code. We need the compilation step to generate pure JavaScript from React code written in JSX.
Very often, we’ll feel the need for a library to address a seemingly common problem that all of us would face. In this book, we’ll use libraries such as body-parser (to parse POST data in the form of JSON, or form data) and ESLint (for ensuring our code follows conventions), all on the server side, and some more like react-select on the client side.
Redux: This is a state management library that also combines the Flux programming pattern. It’s typically used in larger projects where even for a single screen, managing the state becomes complex.
Mongoose: If you are familiar with Object Relational Mapping layers, you may find Mongoose somewhat similar. This library adds a level of abstraction over the MongoDB database layer and lets the developer see objects as such. The library also provides other useful conveniences when dealing with the MongoDB database.
Jest: This is a testing library that can be used to test React applications easily.
Although the MERN stack is a robust, production-ready stack as of today, improvements happen in every single component of the stack at a blistering pace. I have used the latest version of all tools and libraries available at the time of writing the book.
But without doubt, by the time you get to read this book, a lot would have changed and the latest version will not be the same as what I used when writing the book. So, in the instructions for installing any package, I have included the major version of the package.
To avoid surprises due to changes in packages, install the same major version of a package as mentioned in the book rather than the latest version of a package available.
Minor version changes are supposed to be backward compatible. For example, if the book has used version 4.1.0 of a package, and while installing you get the latest as 4.2.0, the code should work without any changes. But there is a small chance that the maintainers of the package have erroneously introduced an incompatible change in a minor version upgrade. So, if you find that something is not working despite copy-pasting the code from the GitHub repository, as a last resort, switch to the exact version as used in the book, which can be found in the package.json file in the root of the repository.
Component | Major Version | Remarks |
|---|---|---|
Node.js | 10 | This is an LTS (long term support) release |
Express | 4 | -- |
MongoDB | 3.6 | Community Edition |
React | 16 | -- |
React Router | 4 | -- |
React Bootstrap | 0.32 | There is no 1.0 release yet, things may break with 1.0 |
Bootstrap | 3 | This is compatible with React Bootstrap 0 (and 1 when released) |
Webpack | 4 | -- |
ESLint | 5 | -- |
Babel | 7 | -- |
Note that the JavaScript specification itself has many versions and the support for various versions and features varies across browsers and Node.js. In this book, we will use all new features supported by the tool that is used to compile JavaScript to the lowest common denominator: ES5. This set of features includes JavaScript features from ES2015 (ECMAScript 2015), ES2016, and ES2017. Collectively, these are called ES2015+.
So now you have a fair idea of the MERN stack and what it comprises. But is it really far superior to any other stack, say, LAMP, MEAN, etc.? By all means, any of these stacks are good enough for most modern web applications. All said and done, familiarity is the crux of productivity in software, so I wouldn’t advise a MERN beginner to blindly start their new project on MERN, especially if they have an aggressive deadline. I’d advise them to choose the stack that they are already familiar with.
But MERN does have its special place. It is ideally suited for web applications that have a large amount of interactivity built into the front-end. Go back and re-read the section on “Why Facebook Built React,” as it will give you some insights. You could perhaps achieve the same with other stacks, but you’ll find that it is most convenient to do so with MERN. So, if you do have a choice of stacks, and the luxury of a little time to get familiar, you may find that MERN is a good choice. I’ll talk about a few things that I like about MERN, and these may help you decide.
The best part about MERN that I like is that there is a single language used everywhere. We use JavaScript for client-side code as well as server-side code. Even if you have database scripts (in MongoDB), you write them in JavaScript. So, the only language you need to know and be comfortable with is JavaScript.
This is kind of true of all other stacks based on MongoDB and Node.js at the back-end, especially the MEAN stack. But what makes the MERN stack stand out is that you don’t even need to know a template language that generates pages. In the React way, the way to programmatically generate HTML (actually DOM elements) is using JavaScript. So, not only do you avoid learning a new language, you also get the full power of JavaScript. This is in contrast to a template language, which will have its own limitations. Of course, you will need to know HTML and CSS, but these are not programming languages, and there is no way you can avoid learning HTML and CSS (not just the markup, but the paradigm and the structure).
Apart from the obvious advantage of not having to switch contexts while writing client-side and server-side code, having a single language across tiers also lets you share code between these. I can think of functions that execute business logic, do validation etc. that can be shared. They need to be run on the client side so that user experience is better by being more responsive to user inputs. They also need to be run on the server side to protect the data model.
When using the MERN stack, object representation is JSON (JavaScript Object Notation) everywhere—in the database, in the application server and on the client, and even on the wire.
I have found that this often saves me a lot of hassle in terms of transformations. No Object Relational Mapping (ORM), not having to force fit an object model into rows and columns, no special serializing and de-serializing code. An Object Document Mapper (ODM) such as mongoose may help enforce a schema and make things even simpler, but the bottom line is that you save a lot of data transformation code.
Further, it just lets me think in terms of native objects and see them as their natural selves even when inspecting the database directly using a shell.
Due to its event-driven architecture and non-blocking I/O, the claim is that Node.js is very fast and a resilient web server.
Although it takes a little getting used to, I have no doubt that when your application starts scaling and receiving a lot of traffic, this will play an important role in cutting costs and savings in terms of time spent in trouble-shooting server CPU and I/O problems.
I’ve already discussed the huge number of npm packages available freely for everyone to use. Most problems that you face will have an npm package already as a solution. Even if it doesn’t fit your needs exactly, you can fork it and make your own npm package.
npm has been developed on the shoulders of other great package managers and has therefore built into it a lot of best practices. I find that npm is by far the easiest to use and fastest package manager I have used up to now. Part of the reason is that most npm packages are so small, due to the compact nature of JavaScript code.
SPAs used to have the problem hat they were not SEO friendly, because a search engine would not make Ajax calls to fetch data or run JavaScript code to render the page. One had to use workarounds like running PhantomJS on the server to pseudo-generate HTML pages, or use Prerender.io services that did the same for us. This introduced additional complexity.
With the MERN stack, serving complete pages out of the server is natural and doesn’t require tools that are after-thoughts. This is possible because React runs on JavaScript, which is the same on the client or the server. When React-based code runs on the browser, it gets data from the server and constructs the page (DOM) in the browser. This is the SPA way of rendering the UI. If we wanted to generate the same page in the server for search engine bots, the same React-based code can be used to get the data from an API server and construct the page (this time, as an HTML) and stream that back to the client. This is called server-side rendering (SSR).

Comparison of the SPA way of doing things and server-side rendering using React
In fact, React Native has taken it to another extreme: it can even produce a mobile application’s UI. I don’t cover React Native in this book, but this fact should give you a feel of how React is structured and what it can do for you in the future.
Not many people like or appreciate this, but I really like the fact that React is a library, not a framework.
A framework is opinionated; it has a set way of doing things. The framework asks you to fill in variations of what it thinks all of us want to get done. A library, on the other hand, gives you tools to construct your application. In the short term, a framework helps a lot by getting most of the standard stuff out of the way. But over time, vagaries of the framework, its assumptions about what we want to get done, and the learning curve will make you wish you had some control over what’s happening under the hood, especially when you have some special requirements.
With a library, experienced architects can design their own applications with complete freedom to pick and choose from the library’s functions and build their own frameworks that fit their application’s unique needs and vagaries. So, for an experienced architect or very unique application needs, a library is better, even though a framework can get you started quickly.
This book lets you experience what it takes, and what it is like, to develop an application using the MERN stack.
In this chapter, we discussed the reasons that make MERN a compelling choice for any web application, which included the advantages of a single programming language throughout the stack, characteristics of a NoSQL database, and the isomorphism of React. I hope these reasons convince you to try out the MERN stack if not adopt it.
Avoid copy-pasting code from the book or the GitHub repository. Instead, type out the code yourself. Resort to copy-pasting only when you are stuck and find that things are not working as expected.
Use the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ) to view code listing and changes; it is more convenient because of the way GitHub shows differences.
Do not rely on the accuracy of code listing in the book, instead, rely on the code in the GitHub repository. If you have to copy-paste, do so from the GitHub repository, not from the book.
Use the same versions of packages and tools as used in this book rather than the latest versions. There may be differences in the latest versions and the version in this book that may cause things to break.
Do not skip the exercises: these are meant to make you think and learn where to look for further resources.
Finally, I hope you are really excited to learn about the MERN stack. So, without much ado, we’ll jump into code right in the next chapter and create the most basic of applications: the Hello World application.
As is customary, we will start with a Hello World application, something that is a bare minimum and uses most of the MERN components. The main purpose of any Hello World is to show the basic characteristics of the technology or stack that we are using, and the tools required to get it up and running.
In this Hello World application, we’ll use React to render a simple page and use Node.js and Express to serve that page from a web server. This will let you learn the fundamentals of these technologies. This will also give you some basic familiarity with nvm, npm, and JSX transformation—some tools that we’ll use a lot as we go along.
To quickly get off the ground , let’s write a simple piece of code in a single HTML file that uses React to display a simple page on the browser. No installations, downloads, or server! All you need is a modern browser that can run the code that we write.
Let’s start creating this HTML file and call it index.html. You can use your favorite editor and save this file anywhere on your file system. Let’s start with the basic HTML tags such as <html>, <head>, and <body>. Then, let’s include the React library.
The type can be any HTML tag such as the string 'div', or a React component (which we will start creating in the next chapter). props is an object containing HTML attributes or custom component properties. The last parameter(s) is zero or more children elements, which again are created using the createElement() function itself.
We are using ES2015+ features of JavaScript in this book, and in this snippet, we used the const keyword. This should work in all modern browsers as is. If you are using an older browser such as Internet Explorer 10, you will need to change const to var. Toward the end of the chapter, we will discuss how to support older browsers, but until then, please use one of the modern browsers to test.
A React element (the result of a React.createElement() call) is a JavaScript object that represents what is shown on screen. Since it can be a nested collection of other elements and can depict everything on the entire screen, it is also called the virtual DOM . Note that this is not yet the real DOM, which is in the browser’s memory and that is why it is called a virtual DOM. It resides in the JavaScript engine’s memory as a deeply nested set of React elements, which are also JavaScript objects. A React element contains not just the details of what DOM elements need to be created, but also some additional information about the tree that helps in optimization.
Each of these React elements needs to be transferred to the real DOM for the user interface to be constructed on screen. To do this, a series of document.createElement() calls needs to be made corresponding to each of the React elements. The ReactDOM does this when the ReactDOM.render() function is called. This function takes in as arguments the element that needs to be rendered and the DOM element that it needs to be placed under.

A Hello World written in React
Try to add a class to the h1 element (you will also need to define the class in a <style> section within the <head> section to test whether it works). Hint: Search for “how to specify class in jsx” in stackoverflow.com. Can you explain this?
Inspect the element variable in the developer console. What do you see? If you were to call this tree something, what would you call it?
Answers are available at the end of the chapter.
The simple element that we created in the previous section was easy to write using React.createElement() calls . But imagine writing a deeply nested hierarchy of elements and components: it can get pretty complex. Also, the actual DOM can’t be easily visualized when using the function calls as it can be visualized if it were plain HTML.
Note that although it is strikingly close to HTML syntax, it is not HTML. Note also that the markup is not enclosed within quotes, so it is not a string that can be used as an innerHTML either. It is JSX, and it can be freely mixed with JavaScript.
Now, given all the differences and the complexity in comparison to HTML, why do you need to learn JSX? What value does it add? Why not write the JavaScript itself directly? One of the things I talked about in the Introduction chapter is that MERN has just one language throughout; isn’t this contrary to that?
As we explore React further, you will soon find that the differences between HTML and JSX are not earth-shaking and they are very logical. You will not need to remember a lot or need to look up things as long as you understand and internalize the logic. Though writing JavaScript directly to create the virtual DOM elements is indeed an option, I find that it is quite tedious and doesn’t help me visualize the DOM.
Further, since you probably already know the basic HTML syntax, writing JSX will probably work best. It is easy to understand how the screen will look when you read JSX, because it is very similar to HTML . So, for the rest of the book, we use JSX.
Although no effort has been spared to ensure that all code listings are accurate, there may be typos or even corrections that did not make it to the book before it went to press. So, always rely on the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ) as the tested and up-to-date source for all code listings, especially if something does not work as expected.
When you test this set of changes, you will find no difference in the appearance of the page, but it could be a wee bit slower due to the compilation done by Babel. But don’t worry. We’ll soon switch to compiling JSX at build-time rather than at runtime to get rid of the performance impact. Note that the code will not work on older browsers yet; you may get errors in the script babel.min.js.
The file index.html can be found under a directory called public in the GitHub repository; this is where the file will eventually end up.
Remove type="text/babel" from the script. What happens when you load index.html? Can you explain why? Put back type="text/babel" but remove the Babel JavaScript library. What happens now?
We used the minified version of Babel, but not for React and ReactDOM. Can you guess why? Switch to the production minified version and introduce a runtime error in React (check out the unpkg.com website for the names of the production versions of these libraries). For example, introduce a typo in the ID of the content node so there is no place to mount the component. What happens?
Answers are available at the end of the chapter.
The server-less setup allowed you to get familiarized with React without any installations or firing up a server. But as you may have noticed, it's good neither for development nor for production. During development, some additional time is introduced for loading the scripts from a Content Delivery Network or CDN. If you take a look at the size of each of the scripts using the Network tab of the browser's developer console, you'll see that the babel compiler (even the minified version) is quite large. On production, especially in larger projects, runtime compilation of JSX to JavaScript will slow down the page loading and affect user experience.
So, let's get a little organized and serve all files from an HTTP server. We will, of course, use some other components of the MERN stack to achieve this. But before we do all that, let’s set up our project and folders in which we will be saving the files and installing libraries.
The commands that we will be typing in the shell have been collected together in a file called commands.md in the root of the GitHub repository.
If you find that something is not working as expected when typing a command, cross-check the commands with the same in the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ). This is because typos may have been introduced during the production of the book, or last-minute corrections may have missed making it to the book. The GitHub repository, on the other hand, reflects the most up-to-date and tested set of code and commands.
To start, let's install nvm. This stands for Node Version Manager, that tool makes installation and switching between multiple versions of Node.js easy. Node.js can be installed directly without nvm, but I've found that having nvm installed at the very beginning made my life easier when I had to start a new project and I wanted to use the latest and greatest version of Node.js at that point in time. At the same time, I did not want to switch to the newest version for some other of my large projects, for fear of breaking things in those projects.
To install nvm, if you are using Mac OS or any Linux-based distribution, follow the instructions on nvm's GitHub page. This can be found at https://github.com/creationix/nvm . Windows users can follow nvm for Windows (search for it in your favorite search engine) or install Node.js directly without nvm. Generally, I advise Windows users to install a Linux virtual machine (VM), preferably using Vagrant, and do all the server-side coding within the VM. This usually works best, especially because the code is finally deployed almost always on a Linux server, and having the same environment for development works best.
One tricky thing about nvm is knowing how it initializes your PATH. This works differently on different operating systems, so make sure you read up the nuances. Essentially, it adds a few lines to your shell's initialization scripts so that the next time you open a shell, your PATH is initialized and executes nvm's initialization scripts. This lets nvm know about the different versions of Node.js that are installed, and the path to the default executable.
For this reason, it's always a good idea to start a new shell right after you install nvm rather than continue in the shell that you installed it. Once you get the right path for your nvm, things follow smoothly.
You may choose to install Node.js directly, without nvm, and that is fine too. But the rest of the chapter assumes that you have installed nvm.
The LTS version is assured to have support for a longer term than other versions. This means that although feature upgrades cannot be expected, security and performance fixes that are backward compatible can be expected. Also newer minor versions can be installed without worrying about breaking existing code.
Also, do make sure that any new shell also shows the newest version. (Note that the Windows version of nvm does not support the alias command. You may have to do nvm use 10 each time you open a new shell.)
Please ensure you don’t miss the –g flag. It tells npm to install itself globally, that is, available to all projects. To double check, run npm --version again .
Before we install any third-party packages with npm, it's a good idea to initialize the project. With npm, even an application is considered a package. A package defines various attributes of the application. One such important attribute is a list of other packages that the application depends upon. This will change over time, as we find a need to use libraries as we make progress with the application.
Most questions that this command asks of you should be easy to answer. The defaults will work fine too. From now on, you should be in the project directory for all shell commands, especially npm commands (which I'll describe next). This will ensure that all changes and installations are localized to the project directory.
Specifying the major version alone (in this case 4) will suffice while installing a package. Which means that you may install a minor version that is not the same as what was used when writing this book. In the rare case that this is causing a problem, look up the specific version of the package within package.json in the GitHub repository. Then, specify that exact while installing the package, for example, npm install express@4.16.4.
All installations are local to the project directory. This means that a different project can use a different version of any of the installed packages. This may, at first, seem unnecessary and feel like a lot of duplication. But you will really appreciate this feature of npm when you start multiple Node.js projects and don't want to deal with a package upgrade that you don't need. Further, you will notice that the entire Express package, including all dependencies, is just 1.8MB. With packages being so tiny, excessive disk usage is not a concern at all.
A package's dependencies are also isolated within the package. Thus, two packages depending on different versions of a common package could be installed, and they would each have their own copy and therefore work flawlessly.
Administrator (superuser) rights are not needed to install a package.
There is, of course, an option to install packages globally, and sometimes it is useful to do this. One use case is when a command-line utility is packaged as an npm package. In such cases, having the command-line available regardless of the working directory is quite useful. In such cases, the –g option of npm install can be used to install a package globally and made available everywhere.
If you had installed Node.js via nvm, a global install will use your own home directory and make the package available to all projects within your home directory. You don’t need superuser or administrator rights to install a package globally. On the other hand, if you had installed Node.js directly, making it available for all users on your computer, you will need superuser or administrator rights.
At this point in time, it is a good idea to look at the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ) again, especially to view the differences from the previous step. In this section we only added new files, so the only differences you will see are the new files.
When was package.json created? If you can't guess, inspect the contents; that should give you a hint. Still can't figure out? Go back and re-do your steps. Start with creation of the project directory and look at the directory contents at each step.
Uninstall Express, but use the option --no-save. Now, just type npm install. What happens? Add another dependency, say, MongoDB, manually to package.json this time. Use version as “latest”. Now, type npm install. What happens?
Use --save-dev while installing any new package. What difference do you see in package.json? What difference do you think it will make?
Where do you think the packages files are installed? Type npm ls --depth=0 to check all the currently installed packages. Clean up any packages you do not need.
Play around with npm installation and un-installation a bit. This will be useful in general. Learn more about npm version syntax from the documentation: https://docs.npmjs.com/files/package.json#dependencies .
Answers are available at the end of the chapter.
Although it is a good practice to check in the package.json.lock file so that the exact versions installed can be shared among team members, I have excluded it from the repository to keep the diffs concise and readable. When you start a team project using the MERN stack, you should check in this file in your Git repository.
Express, if you remember in the introduction in the previous chapter, is the best way to run an HTTP server in the Node.js environment. For starters, we'll use Express to serve only static files. This is so that we get used to what Express does, without getting into a lot of server-side coding. We’ll serve the index.html file that we created in the previous section via Express.
require is a JavaScript keyword specific to Node.js, and this is used to import other modules. This keyword is not part of browser-side JavaScript because there was no concept of including other JavaScript files. All needed scripts were included directly in HTML files only. ES2015 specification came up with a way to include other files using the import keyword, but before the specification came out, Node.js had to invent its own using require. It’s also known as the CommonJS way of including other modules.
In the previous line, we loaded up the module called express and saved the top-level thing that the module exports, in the constant named express. Node.js allows the thing to be a function, an object, or whatever can fit into a variable. The type and form of what the module exports is really up to the module, and the documentation of the module will tell you how to use it.
In the case of Express, the module exports a function that can be used to instantiate an application. We just assigned this function to the variable express .
We used the ES2015 const keyword to define the variable express. This makes the variable non-assignable after the first declaration. For variables that may be assigned a new value, the keyword let can be used in place of const.
Now that we have a handle to the application, let’s set it up. Express is a framework that does minimal work by itself; instead, it gets most of the job done by functions called middleware. A middleware is a function that takes in an HTTP request and response object, plus the next middleware function in the chain. The function can look at and modify the request and response objects, respond to requests, or decide to continue with middleware chain by calling the next middleware function.
The argument to the static() function is the directory where the middleware should look for the files, relative to where the app is being run. For the application we’ll build as part of this book, we’ll store all static files in the public directory under the root of our project. Let’s create this new directory, public, under the project root and move index.html that we created in the previous section to this new directory.
The first argument is optional and defaults to '/' if not specified, so we could skip it too.
Finally, now that the application is set up, we’ll need to start the server and let it serve HTTP requests. The listen() method of the application starts the server and waits eternally for requests. It takes in a port number as the first argument. Let’s use the port 3000, an arbitrary port. We won’t use port 80, the usual HTTP port, because to listen to that port we need to have administrative (superuser) privileges.
Now, we are ready to start the web server and serve index.html. If you are looking at the GitHub repository for the code, you will find server.js under a directory called server. But at this point, the file needs to be in the root of the project directory.
You should see a message saying the application has started on port 3000. Now, open your browser and type http://localhost:3000/index.html in the URL bar. You should see the same Hello World page that we created in the previous section. If you see a 404 error message, maybe you have not moved index.html to the public directory .
The static middleware function served the contents of the index.html file from the public directory, as it matched the request URL. But it is also smart enough to translate a request to / (the root of the website) and respond by looking for an index.html in the directory. This is similar to what other static web servers such as Apache do. Thus, typing just http://localhost:3000/ is good enough to get the Hello World page.
When this is executed, npm looks for the file server.js and runs it using Node.js. So, let’s stop the server (using Ctrl+C on the command shell) and restart the server using npm start. You should see the same message saying the server has been started.
But what if we had a different starting point for the server? In fact, we want all the server-related files to go into a directory called server. So, let’s create that directory and move server.js into that directory.
Thus, if the server starting point is anything other than server.js in the root directory, the full command line has to be specified in the scripts section of package.json.
Note that there is also a field called main in package.json. When we initialized this file, the value of this field was set to index.js automatically. This field is not meant for indicating the starting point of the server. Instead, if the package was a module (as opposed to an application), index.js would have been the file to load when the project is imported as a module using require() in other projects. Since this project is not a module that can be imported in other projects, this field is not of any interest to us, and neither do we have index.js in the source code.
Now, we can use npm start and see the familiar application started message and test it out one final time. At this point in time, it’s good to check out the GitHub repository and look at the diffs for this section. In particular, take a look at changes in an existing file, package.json, to familiarize yourself with how changes within a file are shown in the book and how the same changes are seen in GitHub as diffs.
Change the name of the index.html file to something else, say, hello.html. How does this affect the application?
If you wanted all static files to be accessed by a prefixed URL, for example /public, what change would you make? Hint: Take a look at the Express documentation for static files at https://expressjs.com/en/starter/static-files.html .
Answers are available at the end of the chapter.
In all the previous sections, the transformation of JSX to JavaScript happens at runtime. This is inefficient and quite unnecessary. Let’s instead move the transformation to the build stage in our development, so that we can deploy a ready-to-use distribution of the application.
At this point, the app should continue to work as before. If you point your browser to http://localhost:3000, you should see the same Hello World message. But we have only separated the files; we have not moved the transform to build time. The JSX continues to get transformed by the Babel library script, which was executed in the browser. We’ll move the transform to build time in the next section.
Let’s now create a new directory to keep all the JSX files, which will be transformed into plain JavaScript and into the public folder. Let's call this directory src and move App.jsx into this directory.
If you look at the output directory public, you will see that there is a new file called App.js in there. If you open the file in your editor, you can see that the JSX elements have been converted to React.createElement() calls . Note that the Babel compilation automatically used the extension .js for the output file, which indicates that it is pure JavaScript.
If you test this set of changes, you should see that things work as before. For good measure, you could use the browser's developer console to ensure it is App.js that's being fetched, and not App.jsx. The developer console can be found on most browsers; you may need to look at your browser’s documentation for instructions to get to it.
Inspect the contents of App.js, the output of the transform. You’ll find it in the public directory. What do you see?
Why did we use --save-dev while installing babel-cli? Hint: Read the npm documentation for the CLI command for install at https://docs.npmjs.com/cli/install .
Answers are available at the end of the chapter.
I’d mentioned earlier that the JavaScript code will work in all modern browsers that support ES2015. But what if we need to support older browsers, for example, Internet Explorer? The older browsers don’t support things like Arrow Functions and the Array.from() method . In fact, running the code at this point in IE 11 or earlier should throw a console error message saying Object.assign is not a function.
If you transform this using Babel, restart the server and point a browser at it. You will find that it works on most modern browsers. But, if you look at the transformed file, App.js, you will see that the JavaScript itself has not been changed, only the JSX has been replaced with React.createElement() calls . This is sure to fail on older browsers that neither recognize the arrow function syntax or the Array.from() method .
Now if you inspect the output of the transform, App.js, you will see that arrow functions have been replaced with regular functions.
That’s great, but how does one know which plugins have to be used? It’s going to be tedious to find out which browsers support what syntax and what transforms we’ve to choose for each. Fortunately, Babel does the job of automatically figuring this out via a preset called preset-env. This preset lets us specify the target browsers that we need to support and automatically applies all the transforms and plugins that are required to support those browsers.
Instead of using the command-line (which can get quite lengthy if we do use it), let’s specify the presets that need to be used in a configuration file. Babel looks for this in a file called .babelrc. In fact, there can be a .babelrc file in different directories, and the settings for files in that directory can be specified in each of those separately. Since we have all the client-side code in the directory called src, let’s create this file in that directory.
If you run this command and then inspect the generated App.js, you will find that the arrow function has been replaced with a regular function and also that the string interpolation has been replaced with a string concatenation. If you take out the line ie: "11" from the configuration file and re-run the transform, you’ll find that these transformations are no longer there in the output file, because the browsers that we’re targeting already support these features natively.
But even with these transformations, if you test on an Internet Explorer version 11, the code will still not work. That’s because it’s not just the transformations; there are some built-ins such as Array.find() that just aren’t there in the browser. Note that no amount of compilation or transformation can add a bunch of code like the Array.find() implementation. We really need these implementations to be available at runtime as a library of functions.

The new Hello World screen
Try to format the message as one per line by using <br /> to join the individual messages instead of a space. Are you able to do it? Why not?
Answers are available at the end of the chapter.
Apart from being able to start the project using npm start, npm has the ability to define other custom commands. This is especially useful when there are many command line parameters for the command and typing them out on the shell becomes tedious. (Didn’t I say npm was powerful? This is one of the things that it does, even though this is not real package manager functionality.) These custom commands can be specified in the scripts section of package.json. These can then be run using npm run <script> from the console.
After this, if you run npm start again to start the server, you can see any changes to App.jsx reflected in the application.
Avoid using npm sub-command names that are also npm first-level commands such as build and rebuild, as this leads to silent errors if and when you leave out the run in the npm command.
It is essentially the same command as compile, but with two extra command line options, --watch and --verbose. The first option instructs Babel to watch for changes in source files, and the second one causes it to print out a line in the console whenever a change causes a recompilation. This is just to give a reassurance that a compile has in fact taken place whenever a change is made, provided you keep a watch on the console running this command.
If you now run the new command using npm run watch , you will notice that it does one transform, but it doesn’t return to the shell. It’s actually waiting in a permanent loop, watching for changes to the source files. So, to run the server, another terminal is needed, where npm start can be executed.
If you make make a small change to App.jsx and save the file, you’ll see that App.js in the public directory is regenerated. And, when you refresh the browser, you can see those changes without having to manually recompile. You can also make any changes to server.js and see that the server starts, with a message on the console that says the server is being restarted.
In this chapter, you learned the basics of how React applications can be built. We started with a simple piece of code written in React JSX that we compiled at runtime, then we moved the compilation as well as serving the file to the server.
We used nvm to install Node.js; you saw how npm can be used not just to install Node.js packages, but also to save command-line instructions in conventional or easy-to-spot scripts. We then used Babel, to transpile, that is, transform or compile from one specification of the language to another to support older browsers. Babel also helped us transform JSX into pure JavaScript.
You also got a whiff of what Node.js with Express can do. We did not use MongoDB, the M in the MERN stack, but I hope you got a good view of the other components of the stack.
By now, you should also have gotten familiar with how the book’s GitHub repository is organized and the conventions used in the book. For every section, there is a testable set of code that you can compare your own typed out code with. Importantly, the diffs between each step are valuable to understand the exact changes that were made in each step. Once again, note that the code in the GitHub repository is the one to rely on, with up-to-date changes that couldn’t make it to the printed book. If you find that you have followed the book verbatim, yet things don’t work as expected, do consult the GitHub repository to see if the printed book’s errors have been corrected there.
In the next two chapters, we’ll dive deeper into React, then surface up to look at the big picture, dealing with APIs, MongoDB, and Express in later chapters.
To specify a class in React.createElement(), we need to use { className: <name>} instead of {class: <name>}. This is because class is a reserved word in JavaScript, and we cannot use it as a field name in objects.
The element variable contains a nested tree of elements, which reflects what the DOM would contain. I would call this a virtual DOM, which is what it is indeed popularly referred to as.
Removing the script type will cause the browser to treat it as regular JavaScript and we will see syntax errors on the console because JSX is not valid JavaScript. Removing the Babel compiler instead will cause the script to be ignored, since the browser does not recognize scripts of type text/babel, and it will ignore it. In either case, the application will not work.
A minified version of React hides or shortens runtime errors. A non-minified version gives full errors and helpful warnings as well.
package.json was created when we created the project using npm init. In fact, all our responses to the prompts when we ran npm init were recorded in package.json.
When using --no-save, npm keeps the file package.json unchanged. Thus, package.json would have retained the dependency of Express. Running npm install without any further options or parameters installs all dependencies listed in package.json. Thus, you could add dependencies manually to package.json and just use npm install.
The --save-dev option adds the package in devDependencies instead of dependencies. The list of dev dependencies will not be installed in production, which is indicated by the environment variable NODE_ENV being set to the string production.
Package files are installed under the directory node_modules under the project. npm ls lists all the packages installed, in a tree-like manner. --depth=0 restricts the tree depth to the top-level packages. Deleting the entire node_modules directory is one way of ensuring you start clean.
The static file middleware does not specially treat hello.html as it did index.html, so you will have to access the application with the name of the file like this: http://localhost:3000/hello.html.
For accessing static files via a different mount point, specify that prefix in the middleware generated helper function as the first parameter. For example, app.use('/public', express.static('/public')).
App.js now contains pure JavaScript, with all JSX elements converted to React.createElement() calls. We couldn’t see this transform earlier when the transform happened in the browser.
When we deploy the code, we will only deploy a pre-built version of the application. That is, we will transform the JSX on a build server or our development environment and push out the resulting JavaScript to our production server. Thus, on the production server, we will not need the tools that are required to build the application. Therefore, we used --save-dev so that, on the production server, the package need not be installed.
React does this on purpose, to avoid cross-site scripting vulnerabilities. It is not easy to insert HTML markup, although there is a way using the dangerouslySetInnerHTML attribute of an element. The correct way to do this would be to compose an array of components. We will explore how to do this in later chapters.
In the Hello World example, we created a very basic React native component, using pure JSX. However, in the real world, you will want to do much more than what a simple single-line JSX can do. That is where React components come in. React components can be composed using other components and basic HTML elements; they can respond to user input, change state, interact with other components, and much more.
But, before going into all that detail, let me first describe the application that we will build as part of this book. At every step that we take in this as well as the following chapters, we’ll take features of the application or tasks that need to be performed one by one and address them. I like this approach because I’ve learned things the best when I put them to immediate use. This approach not only lets you appreciate and internalize the concepts because you put them to use, but also brings the more useful and practical concepts to the forefront.
The application I’ve come up with is something that most developers can relate to.
I’m sure that most of you are familiar with GitHub Issues or Jira. These applications help you create a bunch of issues or bugs, assign them to people, and track their statuses. These are essentially CRUD applications (Create, Read, Update, and Delete a record in a database) that manage a list of objects or entities. The CRUD pattern is so useful because pretty much all enterprise applications are built around the CRUD pattern on different entities or objects.
In the case of the Issue Tracker, we’ll only deal with a single object or record, because that’s good enough to depict the pattern. Once you grasp the fundamentals of how to implement the CRUD pattern in MERN, you’ll be able to replicate the pattern and create a real-life application.
The user should be able to view a list of issues, with an ability to filter the list by various parameters.
The user should be able to add new issues, by supplying the initial values of the issue’s fields.
The user should be able to edit and update an issue by changing its field values.
The user should be able delete an issue.
A title that summarizes the issue (freeform long text)
An owner to whom the issue is assigned (freeform short text)
A status indicator (a list of possible status values)
Creation date (a date, automatically assigned)
Effort required to address the issue (number of days, a number)
Estimated completion date or due date (a date, optional)
Note that I’ve included different types of fields (lists, date, number, text) to make sure you learn how to deal with different data types. We’ll start simple, build one feature at a time, and learn about the MERN stack as we go along.
In this chapter, we’ll create React classes and instantiate components. We’ll also create bigger components by putting together smaller components. Finally, we’ll pass data among these components and create components dynamically from data. In terms of features, the objective in this chapter is to lay out the main page of the Issue Tracker: a list of issues. We’ll hard-code the data that is used to display the page and leave retrieval of the data from the server to a later chapter.
In this section, the objective is to convert the single-line JSX into a simple React component instantiated from a React class, so that we can later use the full power of the first class React components.
React classes are used to create real components (as opposed to templated HTML where we created a Hello World message based on a variable, which we created in the previous chapter). These classes can then be reused within other components, handle events, and so much more. To start with, let’s replace the Hello World example with a simple class forming the starting point for the Issue Tracker application.
React classes are created by extending React.Component, the base class from which all custom classes must be derived. Within the class definition, at the minimum, a render() method is needed. This method is what React calls when it needs to display the component in the UI.
There are other methods with special meaning to React that can be implemented, called the Lifecycle methods. These provide hooks into various stages of the component formation and other events. We’ll discuss other Lifecycle functions in later chapters. But render() is one that must be present, otherwise the component will have no screen presence. The render() function is supposed to return an element (which can be either a native HTML element such as a <div> or an instance of another React component).
We used the ES2015 class keyword and the extends keyword to define a JavaScript class. React recommends the use of ES2015 classes. If you are not familiar with JavaScript classes, read up and learn about classes starting at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes .
In essence, the JSX element is now returned from the render() method of the component class called Hello World. The brackets around the JSX representation of the Hello World element are not necessary, but it is a convention that is normally used to make the code more readable, especially when the JSX spans multiple lines.
Now, this element can be used in place of the <div> element to render inside the node called contents, as before. It’s worth noting here that div and h1 are built-in internal React components or elements that could be directly instantiated. Whereas HelloWorld is something that we defined and later instantiated. And within HelloWorld, we used React’s built-in div component. The new, changed App.jsx is shown in Listing 3-1.
By now you should be running npm run watch in a console and have started the server using npm start in a separate console. Thus, any changes to App.jsx should have been automatically compiled. So, if you refresh your browser, you should see the greeting with all the continents, just as before.
In the render function, instead of returning one <div>, try returning two <div> elements placed one after the other. What happens? Why, and what’s the solution? Ensure you look at the console running npm run watch.
Create a runtime error for the React library by changing the string 'contents' to 'main' or some other string that doesn’t identify an element in the HTML. Where can one see the error? What about JavaScript runtime errors like undefined variable references?
Answers are available at the end of the chapter.
In the previous section, you saw how to build a component by putting together built-in React components that are HTML element equivalents. It’s possible to build a component that uses other user-defined components as well, and that’s what we will explore in this section.
Component composition is one of the most powerful features of React. This way, the UI can be split into smaller independent pieces so that each piece can be coded and reasoned in isolation, making it easier to build and understand a complex UI. Using components rather than building the UI in a monolithic fashion also encourages reuse. We’ll see in a later chapter how one of the components that we built could easily be reused, even though we hadn’t thought of reuse at the time of building the component.
Larger components should be split into fine-grained components when there is a logical separation possible between the fine-grained components. In this section we’ll create logically separated components.
When there is an opportunity for reuse, components can be built which take in different inputs from different callers. When we build specialized widgets for user inputs in Chapter 10, we will be creating reusable components.
React’s philosophy prefers component composition in preference to inheritance. For example, a specialization of an existing component can be done by passing properties to the generic component rather than inheriting from it. You can read more about this at https://reactjs.org/docs/composition-vs-inheritance.html .
In general, remember to keep coupling between components to a minimum (coupling is where one component needs to know about the details of another component, including parameters or properties passed between them).

Structure of the Issue List page
Now let’s add a render() method . Within this method, let’s add an instance of each of the new placeholder classes separated by a <hr> or horizontal line. As you saw in the exercise of the earlier section, since the return of render() has to be a single element, these elements have to be enclosed within a <div> or a React Fragment component. The Fragment component is like an enclosing <div> but it has no effect on the DOM.
Ideally, each component should be written as an independent file. But at the moment, we only have placeholders, so for the sake of brevity, we’ll keep all the classes in the same file. Also, you haven’t learned how to put multiple class files together. At a later stage, when the classes are expanded to their actual content and we also have a way of building or referring to once class from another, we’ll separate them out.

Issue Tracker by composing components
Inspect the DOM in the developer console. Do you see any HTML element corresponding to the React.Fragment component? How do you think this can be useful as compared to using a <div> element to enclose the various elements?
Answers are available at the end of the chapter.

Issue List UI hierarchy with issue rows
JSX does not support comments natively. In order to add comments, a JavaScript snippet has to be added that has JavaScript style comments. Thus, the form {/* ... */} can be used to place comments within JSX. Using HTML style comments like <!-- ... --> will not work.
In fact, the way to switch to the JavaScript world within any JSX snippet is to use curly braces. In the previous chapter, we used this to display the Hello World message, which was a JavaScript variable called message using the syntax {message}.
In this case, we passed across a simple string. Other data types and even JavaScript objects can be passed this way. Any JavaScript expression can be passed along, by using curly braces ({}) instead of quotes, because the curly braces switches into the JavaScript world.
So, let’s pass the issue’s title (as a string), its ID (as a number), and the row style (as an object) from IssueTable to IssueRow. Within the IssueRow class, we’ll use these passed-in properties to display the ID and title and set the style of the row, by accessing these properties through this.props.
We used the attribute style for the table-cell just as we would have used it in regular HTML. But note that this is not really an HTML attribute. Instead, it is a property being passed on to the built-in React component <td>. It’s just that the style property in the td component is interpreted and set as the HTML style attribute. In most cases, like style, the name of the attribute is the same as the HTML attribute, but for a few attributes causing conflict with JavaScript reserved words, naming requirements are different. Thus, the class HTML attribute needs to be className in JSX. Also, hyphens in the HTML attributes need to be replaced by camel cased names, for example, max-length becomes maxLength in JSX.
A complete list of DOM elements and how attributes for these need to be specified can be found in the React Documentation at https://reactjs.org/docs/dom-elements.html .
Now that we have an IssueRow component receiving the properties, let’s pass them from the parent, IssueTable. The ID and the title are straightforward, but the style we need to pass on has a special convention of specification in React and JSX.
Rather than a CSS kind of string, React needs it to be specified as an object with a specific convention containing a series of JavaScript key-value pairs. The keys are the same as the CSS style name, except that instead of dashes (like border-collapse), they are camel cased (like borderCollapse). The values are CSS style values, just as in CSS. There is also a special shorthand for specifying pixel values; you can just use a number (like 4) instead of a string "4px".
Note that we are not using string-like quotes for the Issue ID since it is a number or for rowStyle since it is an object. We instead use the curly braces, which makes it a JavaScript expression.
Now, let’s construct the IssueTable component, which is essentially a <table>, with a header row and two columns (ID and title), and two hard-coded IssueRow components. Let’s also specify an inline style for the table to indicate a collapsed border and use the same rowStyle variable to specify the header row styles, to make it look uniform.
Try adding an attribute border=1 for the table, as we would in regular HTML. What happens? Why? Hint: Read up the section titled “All Supported HTML Attributes” in the “DOM Elements” section of the React API Reference.
Why is there a double curly brace in the inline style for the table? Hint: Compare it to the other style, where we declared a variable and used that instead of specifying it inline.
The curly braces are a way to escape into JavaScript in the middle of JSX markup. Compare this to similar techniques in other templating languages such as PHP.
Answers are available at the end of the chapter.
There is another way to pass data to other components, using the contents of the HTML-like node of the component. In the child component, this can be accessed using a special field of this.props called this.props.children.
Just like in regular HTML, React components can be nested. In the Hello World example, we nested a <h1> element inside a <div>. When the components are converted to HTML elements, the elements nest in the same order. React components can act like the <div> and take in nested elements. In such cases, the JSX expression will need to include both the opening and closing tags, with child elements nested in them.

Passing data to child components
When is it appropriate to pass data as props vis-à-vis children? Hint: Think about what is it that we want to pass.
Answers are available at the end of the chapter.
In this section, we’ll replace our hard-coded set of IssueRow components with a programmatically generated set of components from an array of issues. In later chapters we’ll get more sophisticated by fetching the list of issues from a database, but for the moment, we’ll use a simple in-memory JavaScript array to store a list of issues.
You can add more example issues, but two issues are enough to demonstrate dynamic composition. Now, let’s modify the IssueTable class to use this array of issues rather than the hard-coded list. Within the IssueTable class’ render() method, let’s iterate over the array of issues and generate an array of IssueRows from it.
In other frameworks and templating languages, creating multiple elements using a template would have required a special for loop construct (e.g., ng-repeat in AngularJS) within that templating language. But in React, regular JavaScript can be used for all programmatic constructs. This not only gives you the full power of JavaScript to manipulate templates, but also a lesser number of constructs to learn and remember.
Now, we can remove rowStyle from all the table-cells and table-headers. One last thing that needs to be done is to identify each instance of IssueRow with an attribute called key. The value of this key can be anything, but it has to uniquely identify a row. React needs this key so that it can optimize the calculation of differences when things change, for example, when a new row is inserted. We can use the ID of the issue as the key, as it uniquely identifies the row.

Issue Rows constructed programmatically from an array
We used the issue’s id field as the value of key. What other keys could have been used? Which one would you choose?
In the previous section, we passed every field of an issue as a separate property to IssueRow. In this section, we passed the entire issue object. Why?
Instead of using a local variable issueRows, try using the map expression directly inside the <tbody>. Does it work? What does it tell us?
Answers are available at the end of the chapter.
In this chapter, we created a barebones version of the main page of the Issue Tracker. We started using React classes instead of simple elements, some of which were just placeholders to depict components that we have not yet developed. We did this by writing fine-grained individual components and putting them together (composing) in an enclosing component. We also passed parameters or data from an enclosing component to its children, and thus reused a component class and rendered it differently with different data, dynamically using a map() to generate components based on an array of input data.
The components didn’t do much apart from rendering themselves based on the input data. In the next chapter, we’ll see how user interaction can affect the data and change the appearance of a component.
Compilation will fail with an error, “Adjacent JSX elements must be wrapped in an enclosing tag”. The render() method can only have a single return value, thus, it can return only one element. Enclosing the two <div>s in another <div> is one solution, or as the error message suggests, using a Fragment component is another solution, which we will discuss in later sections.
React prints an error in the browser’s JavaScript console when it is a React error. Regular JavaScript errors are also shown in the console, but the code displayed is not the original code; it is the compiled code. We’ll learn how to debug using the original source in later chapters.
No, there is no enclosing element. All the elements returned by IssueList appear directly under the contents div. In this case, we could have easily used a <div> to enclose the elements.
Then, the calling component would place these rows under a <tbody> element. Adding a <div> to enclose these rows would result in an invalid DOM tree, as <tbody> cannot have a <div> within it. A fragment is the only option in this case.
A border will not be displayed. How React interprets each element’s attribute is different from how an HTML parser does it. The border attribute is not one of the supported attributes. React completely ignores the border attribute.
The outer braces denote that the attribute value is a JavaScript expression. The inner braces specify an object, which is the attribute’s value.
The curly braces of React are similar to <?php ... ?> of PHP, with a slight difference. The contents within a <?php ... ?> tag are full-fledged programs, whereas in JSX, you can only have JavaScript expressions. All programming constructs like for loops are written outside the JSX in plain JavaScript.
props are flexible and useful for passing in any kind of data. On the other hand, children can only be an element, which can also be deeply nested. Thus, if you have simple data, pass it as props. If you have a component to pass, you could use children if it is deeply nested and naturally appears within the child component. Components can also be passed as props, typically when you want to pass multiple components or when the component is not a natural child content of the parent.
Another choice for the key property is the array index, as it is also unique. If the key is a large value like a UUID, you may think that it is more efficient to use the array index, but in reality it is not. React uses the key to identify the row. If it finds the same key, it assumes it is the same row. If the row has not changed, it does not re-render the row.
Thus, if you insert a row, React will be more efficient in shuffling existing rows rather than re-rendering the entire table if the rows’ keys were the ID of the object. If you used the array index instead, it would think that every row after the inserted row has changed and re-render each of them.
Passing the entire object is obviously more concise. I would choose to pass individual properties only if the number of properties being passed is a small subset of the full set of properties of the object.
It works, despite the fact that we have JSX within the expression. Anything within the curly braces is parsed as a JavaScript expression. But since we are using a JSX transform on JavaScript expressions, these snippets will also go through the transform. It is possible to nest this deeper and use another set of curly braces within the nested piece of JSX and so on.
Until now, we only saw static components, that is, components that did not change. To make components that respond to user input and other events, React uses a data structure called state in the component. In this chapter, we will explore how to use React State and how it can be manipulated to change how the component looks and what it shows on screen.
The state essentially holds the data, something that can change, as opposed to the immutable properties in the form of props that you saw earlier. This state needs to be used in the render() method to build the view. It is only the change of state that can change the view. When data or the state changes, React automatically rerenders the view to show the new changed data.
For this chapter, the goal is to add a button and append a row to the initial list of issues on click of that button. We’ll add this button in place of the placeholder text in the IssueAdd component. By doing that, you’ll learn about a component’s state, how to manipulate it, how to handle events, and how to communicate between components.
We’ll start by appending a row without user interaction. We’ll do this using a timer rather than a button so that we focus on the state and modifications and not deal with things like user input. Toward the end of the chapter, we will replace the timer with an actual button and a form for user input.
The state of a component is captured in a variable called this.state in the component’s class, which should be an object consisting of one or more key-value pairs, where each key is a state variable name and the value is the current value of that variable. React does not specify what needs to go into the state, but it is useful to store in the state anything that affects the rendered view and can change due to any event. These are typically events generated due to user interaction.
For the IssueTable component, the list of issues being displayed is definitely one such piece of data that both affects the rendered view and can also change when an issue is added, edited, or deleted. The array of issues is therefore an ideal state variable.
Other things, such as the size of the window, also can change, but this is not something that affects the DOM. Even though the display changes (for example, a line may wrap because the window is narrower), the change is handled by the browser directly based on the same DOM. So, we don’t need to capture this in the state of the component. There may be cases where it does affect the DOM; for example, if the height of the window determines how many issues we display, we may store the height of the window in a state variable and restrict the number of IssueRow components being constructed. In those cases, the height of the window, or a derived value, for example, the number of issues being shown, could also be stored in the state.
Things that don’t change, for example, the border style of the table, also don’t need to go into the state. That’s because user interaction or other events do not affect the style of borders.
Note that we used only one state variable called issues. We can have other state variables, for instance if we were showing the issue list in multiple pages, and we wanted to also keep the page number currently being shown as another state variable, we could have done that by adding another key to the object like page: 0.
Running and testing this piece of code should show no change in the application; you will still see a table containing two rows of issues, just as before.
If you needed to display each row in a different background color based on the status of the issue, how would you go about it? Would you have a list of colors corresponding to each issue also stored in the state? Why or why not?
Answers are available at the end of the chapter.
Although we set the initial state in the constructor, it is highly unlikely that regular SPA components will have the initial state available to them statically. These will typically be fetched from the server. In the case of the Issue Tracker application, even the initial list issues to be displayed would have to be fetched via an API call.
If there were additional state variables, setting only one variable (issues) will cause it to get merged with the existing state. For example, if we had stored the current page as another state variable, the new value of the state variable issues will be merged into the state, keeping the value of the current page unchanged.
The timeout value of 500 milliseconds is somewhat arbitrary: it’s reasonable to expect a real API call to fetch the initial list of issues within this time.
Now, it is very tempting to call loadData() within the constructor of IssueTable. It may even seem to work, but the fact is that the constructor only constructs the component (i.e., does all the initialization of the object in memory) and does not render the UI. The rendering happens later, when the component needs to be shown on the screen. If this.setState() gets called before the component is ready to be rendered, things will go awry. You may not see this happening in simple pages, but if the initial page is complex and takes time to render, and if the Ajax call returns before rendering is finished, you will get an error.
componentDidMount() : This method is called as soon as the component’s representation has been converted and inserted into the DOM. A setState() can be called within this method.
componentDidUpdate() : This method is invoked immediately after an update occurs, but it is not called for the initial render. this.setState() can be called within this method. The method is also supplied the previous props and previous state as arguments, so that the function has a chance to check the differences between the previous props and state and the current props and state before taking an action.
componentWillUnmount() : This method is useful for cleanup such as cancelling timers and pending network requests.
shouldComponentUpdate() : This method can be used to optimize and prevent a rerender in case there is a change in the props or state that really doesn’t affect the output or the view. This method is rarely used because, when the state and props are designed well, there will rarely be a case when the state or props change but an update is not required for the view.

Empty table shown for a fraction of a second
It gets filled soon after, but still, there is a flicker. When we explore server-side rendering in later chapters, we will get rid of this ungainly flicker. For the moment, let’s live with this minor UI unpleasantness.
In the previous sections, you saw how to set the initial state, using a direct assignment in the constructor as well as setting a value in other lifecycle methods using this.setState(). In this section, let’s make a minor change to the state rather than set a completely new value to it. Let’s add a new issue and thus change, not the complete state, but only a portion of it.
It may seem to work, but it will have unexpected consequences in some of the lifecycle methods within this as well as descendent components. Especially in those methods that compare the old and new properties, the difference between the old state and the new one will not be detected.
What is needed in the setState() call is a fresh array of issues, say a copy of the state variable. If any existing array element, say an issue itself, is changing, not only is the copy of the array needed, but also the copy of the object that is being changed is needed. There are libraries called immutability helpers, such as immutable.js ( http://facebook.github.io/immutable-js/ ), which can be used to construct the new state object. When a property of the object is modified, the library creates a copy optimally.
But we will only append an issue, and not change an existing issue. It’s fairly straightforward to make a shallow copy of the array, and this will suffice for the moment. So, we won’t be using the library—there isn’t much extra code we need to write to handle it. If, in your application, you find that you have to make lots of copies because of deep nesting of objects in the state, you could consider using immutable.js.

Appended row to initial set of issues
Note that we did not explicitly call a setState() on the IssueRow components. React automatically propagates any changes to child components that depend on the parent component’s state. Further, we did not have to write any code for inserting a row into the DOM. React calculated the changes to the virtual DOM and inserted a new row.

Setting state and passing data as props
Set up another timer at say, three seconds, right after the first timer to add yet another issue based on sampleIssue. Do you notice something going wrong when the second new issue is added? Hint: Look at the ID of the first new issue. Why do you think this is happening? How can you correct it?
Add a console.log in the IssueRow’s render() method. How many times do you expect render() to be called? How many console logs do you see? (Make sure you undo the changes you did in the previous exercise!)
Answers are available at the end of the chapter.
Before we add user interface elements to create new issues, let’s move the initiation of the creation to where it really belongs: in the IssueAdd component. This will allow us to deal with the changes one step at a time, because moving the timer for adding a new issue from the IssueTable component to the IssueAdd component is not as trivial as it first appears.
If you do try to move it, you will immediately realize that the createIssue() method will also have to move, or we need to have a variant within IssueAdd that can communicate back to IssueTable and call the createIssue() method, which continues to remain there. But there is no straightforward way to communicate between siblings in React. Only parents can pass information down to children; horizontal communication seems hard, if not impossible.
The way around this is to have the common parent contain the state and all the methods that deal with this state. By lifting the state up on level to IssueList, information can be propagated down to IssueAdd as well as to IssueTable.
We still have to take care of one more thing before we can say we are done with this set of changes. All this while, we have been using the arrow function syntax to set up timers. In ES2015, the arrow function has the effect of setting the context (the value of this) to the lexical scope. This means that this within the callback will refer to whatever this was in the lexical scope, that is, outside of the anonymous function, where the code is present.
That worked as long as the called function was within the same class as the timer callback. It still works, in the loadData() method, because this refers to the IssueList component where the timer was fired, and therefore, this.state refers to the state within IssueList itself.
But, when createIssue is called from a timer within IssueAdd, this will refer to the IssueAdd component. But what we really want is for createIssue to be always called with this referring to the IssueList component. Otherwise, this.state.issues will be undefined.
But then, if we need to ever refer to the same method again and pass it to some other child component, we’d have to repeat this code. Also, there is never going to be a case where we will need the method to be not bound, so it is best to replace the definition of createIssue with a bound version of itself. The recommended way to do this is in the constructor of the class where this method is implemented.
The effect of these changes will not be seen in the user interface. The application will behave as it used to. On refreshing the browser, you will see an empty table to start with, which will soon be populated with two issues and after two seconds, another issue will be added.
But this sets us up nicely for the change where we can replace the timer in IssueAdd with a button that the user can click to add a new issue.
Remove the binding of the method createIssue(). What error do you see in the console? What does it tell you?
Answers are available at the end of the chapter.
Let’s now add an issue interactively, on the click of a button rather than use a timer to do this. We’ll create a form with two text inputs and use the values that the user enters in them to add a new issue. An Add button will trigger the addition.

IssueAdd placeholder replaced with a form
At this point, clicking Add will submit the form and fetch the same screen again. That’s not what we want. Firstly, we want it to call createIssue() using the values in the owner and title fields. Secondly, we want to prevent the form from being submitted because we will handle the event ourselves.
To handle events such as onclick and onsubmit, the properties that we need to supply to the elements are, simply, onClick and onSubmit. As in plain HTML and JavaScript, these properties take functions as values. We’ll create a class method called handleSubmit() to receive the submit event from the form when the Add button is clicked. Within this method, we’ll need a handle to the form, so as in regular HTML, let’s give the form a name, say, issueAdd which can then be referred to in JavaScript using document.forms.issueAdd.
At this point, we are using the conventional way of taking user input, using named inputs and getting their value using the value property of the DOM element. React has another way of dealing with user input by way of controlled components, where the value of the input is tied to a state variable. We’ll explore this in later chapters.

Adding new issues using the IssueAdd form

Component hierarchy and data flow after lifting state up
Refresh the browser; you’ll see that the added issues are gone. How does one persist the changes?
Remove e.preventDefault(). Click the Add button with some values for owner and title. What happens? What do you see in the URL bar? Can you explain this?
Use the developer console to inspect the table and add a breakpoint on the <tbody> element as “break on subtree modification”. Now, add a new issue. How many times is the subtree being modified? Compare this with exercise #2 in “Updating State,” where you traced the number of render() calls in a IssueRow.
Answers are available at the end of the chapter.
We have three functioning React components (IssueAdd, IssueRow and IssueTable) composed hierarchically into IssueList (another one, the IssueFilter, is still a placeholder). But there is a difference among these functioning component classes.
IssueList has lots of methods, a state, initialization of the state, and functions that modify the state. In comparison, IssueAdd has some interactivity, but no state1. But, if you notice, IssueRow and IssueTable have nothing but a render() method. For performance reasons and for clarity of code, it is recommended that such components are written as functions rather than classes: a function that takes in props and just renders based on it. It’s as if the component’s view is a pure function of its props, and it is stateless. The render() function itself can be the component.
Most beginners will have a bit of confusion between state and props, when to use which, what granularity of components should one choose, and how to go about it all. This section is devoted to discussing some principles and best practices.
Both state and props hold model information, but they are different. The props are immutable, whereas state is not. Typically, state variables are passed down to child components as props because the children don’t maintain or modify them. They take in a read-only copy and use it only to render the view of the component. If any event in the child affects the parent’s state, the child calls a method defined in the parent. Access to this method should have been explicitly given by passing it as a callback via props.
Anything that can change due to an event anywhere in the component hierarchy qualifies as being part of the state. Avoid keeping computed values in the state; instead, simply compute them when needed, typically inside the render() method.
Do not copy props into state, just because props are immutable. If you feel the need to do this, think of modifying the original state from which these props were derived. One exception is when props are used as initial values to the state, and the state is truly disjointed from the original state after the initialization.
Attribute | State | Props |
|---|---|---|
Mutability | Can be changed using this.setState() | Cannot be changed |
Ownership | Belongs to the component | Belongs to an ancestor, the component gets a read-only copy |
Information | Model information | Model information |
Affects | Rendering of the component | Rendering of the component |
Split the application into components and subcomponents. Typically, this will reflect the data model itself. For example, in the Issue Tracker, the issues array was represented by the IssueTable component, and each issue was represented by the IssueRow component.
Decide on the granularity just as you would for splitting functions and objects. The component should be self-contained with minimal and logical interfaces to the parent. If you find it doing too many things, just like in functions, it should probably be split into multiple components, so that it follows the Single Responsibility principle (that is, every component should be responsible for one and only one thing). If you are passing in too many props to a component, it is an indication that either the component needs to be split, or it need not exist: the parent itself could do the job.
Communication between components depends on the direction. Parents communicate to children via props; when state changes, the props automatically change. Children communicate to parents via callbacks.
Siblings and cousins can’t communicate with each other, so if there is a need, the information has to go up the hierarchy and then back down. This is called lifting the state up. This is what we did when we dealt with adding a new issue. The IssueAdd component had to insert a row in IssueTable. It was achieved by keeping the state in the least common ancestor, IssueList. The addition was initiated by IssueAdd and a new array element added in IssueList’s state via a callback. The result was seen in IssueTable by passing the issues array down as props from IssueList.
If there is a need to know the state of a child in a parent, you’re probably doing it wrong. Although React does offer a way using refs, you shouldn’t feel the need if you follow the one-way data flow strictly: state flows as props into children, events cause state changes, which flows back as props.
In a well-designed application, most components would be stateless functions of their properties. All states would be captured in a few components at the top of the hierarchy, from where the props of all the descendants are derived.
We did just that with the IssueList, where we kept the state. We converted all descendent components to stateless components, relying only on props passed down the hierarchy to render themselves. We kept the state in IssueList because that was the least common component above all the descendants that depended on that state. Sometimes, you may find that there is no logical common ancestor. In such cases, you may have to invent a new component just to hold the state, even though visually the component has nothing.
In this chapter, you learned how to use state and make changes to it on user interactions or other events. The more interesting aspect was how state values are propagated down the component hierarchy as props. You also had a glimpse of user interaction: the click of a button to add a new issue, and how that causes the state to change, and in turn, how the props in the descendant components changed, causing them to rerender as well. Further, you learned how a child can communicate with its parent via callbacks.
We used simulated asynchronous calls and data local to the browser to achieve all this. In the next chapter, instead of using local data, we’ll fetch the data from the server. When an issue is added, we’ll send the data to the server to persist it.
You could store the background color of each row as part of the state, but then, the values will have to be calculated at some point in time. When is a good time to do this? Just before setting the state? How about when setting the initial state?
Since this is a derived value, it is better and more efficient to just calculate these values within the render() method and use them then and there rather than save them in the state.
When the second timer fires and another issue is added, you will find that it gets an ID of 4 but the ID of the third row also changes to 4. Further, in the console, you will see an error to the effect that two children with the same key were found.
This happens because we are using the same object as the first to create the second issue, and setting the ID to 4 sets it in the one and only object: sampleIssue. To avoid this, you have to create a copy of the object before using it to create a new issue, say, using Object.assign().
Each row is rendered once when initialized (two renders, one for each row). After the new row is inserted, each row is rendered once again (three renders, one for each row). Although a render is called, this does not mean that the DOM is updated. Only the virtual DOM is recreated on each render. Real DOM update happens only where there are differences.
On removing the bind() call, you’ll see an error that says undefined is not an object, while evaluating this.state.issues. This should tell you that this.state is undefined, and lead you to think about whether this is the correct this in this call sequence.
In future, if you see a similar error, it should trigger off a thought that maybe a bind() call is missing somewhere.
To persist the changes, we could either save the issues in local storage on the browser, or save it in the server. Modifying the global initialIssues variable will not work because when the page is refreshed, this variable is recreated.
The page is refreshed as if a new request to / has been made. In the URL bar, you can see URL query parameters for owner and title like ?owner=&title=. This is because the default action of a form is a GET HTTP request with the values of the form, and what you see in the URL bar is just the result of this call. (The values in the URL parameters are blank because they were assigned to empty strings in handleSubmit()).
You will see that the subtree under <tbody> is being modified only once. In the details of the modification, you can see that a child is being added, but none of the other existing children are being modified. If you compare it with the number of render() method calls, you will find that even though render() was being called for each row, only the new row is being added to the DOM.
Now that you have learned about creating components and building a workable user interface using React, in this chapter, we’ll spend some time integrating with the back-end server for the data.
Until now, the only resource the Express and Node.js server was serving was static content in the form of index.html. In this chapter, we’ll start fetching and storing the data using APIs from the Express and Node.js server in addition to the static HTML file. This will replace the hard-coded array of issues in the browser’s memory. We will be making changes to both front-end and back-end code, as we’ll be implementing as well as consuming the APIs.
We will not persist the data on disk; instead, we’ll just use a simulated database in the server’s memory. We will leave actual persistence to the next chapter.
I briefly touched upon Express and how to serve static files using Express in the Hello World chapter. But Express can do much more than just serve static files. Express is a minimal, yet, flexible web application framework. It’s minimal in the sense that by itself, Express does very little. It relies on other modules called middleware to provide the functionality that most applications will need.
The first concept is that of routing . At the heart of Express is a router, which essentially takes a client request, matches it against any routes that are present, and executes the handler function that is associated with that route. The handler function is expected to generate the appropriate response.
A route specification consists of an HTTP method (GET, POST, etc.), a path specification that matches the request URI, and the route handler. The handler is passed in a request object and a response object. The request object can be inspected to get the various details of the request, and the response object’s methods can be used to send the response to the client. All this may seem a little overwhelming, so let’s just start with a simple example and explore the details.
When a request is received, the first thing that Express does is match the request to one of the routes. The request method is matched against the route’s method. In the previous example, the route’s method is get() so any HTTP request using the GET method will match it. Further, the request URL is matched with the path specification, the first argument in the route, which is /hello. When a HTTP request matches this specification, the handler function is called. In the previous example, we just responded with a text message.
The route’s method and path need not be specific. If you want to match all HTTP methods, you could write app.all(). If you needed to match multiple paths, you could pass in an array of paths, or even a regular expression like '/*.do' will match any request ending with the extension .do. Regular expressions are rarely used, but route parameters are often used, so I’ll discuss that in a little more detail.
Route parameters are named segments in the path specification that match a part of the URL. If a match occurs, the value in that part of the URL is supplied as a variable in the request object.
The URL /customers/1234 will match the route specification, and so will /customers/4567. In either case, the customer ID will be captured and supplied to the handler function as part of the request in req.params, with the name of the parameter as the key. Thus, req.params.customerId will have the value 1234 or 4567 for each of these URLs, respectively.
The query string is not part of the path specification, so you cannot have different handlers for different parameters or values of the query string.
Multiple routes can be set up to match different URLs and patterns. The router does not try to find a best match; instead, it tries to match all routes in the order in which they are installed. The first match is used. So, if two routes are possible matches to a request, it will use the first defined one. So, the routes have to be defined in the order of priority.
Thus, if you add patterns rather than very specific paths, you should be careful to add the more generic pattern after the specific paths in case a request can match both. For example, if you want to match everything that goes under /api/, that is, a pattern like /api/*, you should add this route only after all the more specific routes that handle paths such as /api/issues.
Once a route is matched, the handler function is called, which in the previous example was an anonymous function supplied to the route setup function. The parameters passed to the handler are a request object and a response object. The handler function is not expected to return any value. But it can inspect the request object and send out a response as part of the response object based on the request parameters.
Let’s briefly look at the important properties and methods of the request and response objects.
req.params: This is an object containing properties mapped to the named route parameters as you saw in the example that used :customerId. The property’s key will be the name of the route parameter (customerId in this case) and the value will be the actual string sent as part of the HTTP request.
req.query: This holds a parsed query string. It’s an object with keys as the query string parameters and values as the query string values. Multiple keys with the same name are converted to arrays, and keys with a square bracket notation result in nested objects (e.g., order[status]=closed can be accessed as req.query.order.status).
req.header, req.get(header): The get method gives access to any header in the request. The header property is an object with all headers stored as key-value pairs. Some headers are treated specially (like Accept) and have dedicated methods in the request object for them. That’s because common tasks that depend on these headers can be easily handled.
req.path: This contains the path part of the URL, that is, everything up to any ? that starts the query string. Usually, the path is part of the route specification, but if the path is a pattern that can match different URLs, you can use this property to get the actual path that was received in the request.
req.url, req.originalURL: These properties contain the complete URL, including the query string. Note that if you have any middleware that modifies the request URL, originalURL will hold the URL as it was received, before the modification.
req.body: This contains the body of the request, valid for POST, PUT, and PATCH requests. Note that the body is not available (req.body will be undefined) unless a middleware is installed to read and optionally interpret or parse the body.
There are many other methods and properties; for a complete list, refer to the Request documentation of Express at http://expressjs.com/en/api.html#req as well as Node.js’ request object at https://nodejs.org/api/http.html#http_class_http_incomingmessage , from which the Express Request is extended.
res.send(body): You already saw the res.send() method briefly, which responded with a string. This method can also accept a buffer (in which case the content type is set as application/octet-stream as opposed to text/html in case of a string). If the body is an object or an array, it is automatically converted to a JSON string with an appropriate content type.
res.status(code): This sets the response status code. If not set, it is defaulted to 200 OK. One common way of sending an error is by combining the status() and send() methods in a single call like res.status(403).send("Access Denied").
res.json(object): This is the same as res.send(), except that this method forces conversion of the parameter passed into a JSON, whereas res.send() may treat some parameters like null differently. It also makes the code readable and explicit, stating that you are indeed sending out a JSON.
res.sendFile(path): This responds with the contents of the file at path. The content type of the response is guessed using the extension of the file.
There are many other methods and properties in the response object; you can look at the complete list in the Express documentation for Response at http://expressjs.com/en/api.html#res and Node.js’ Response object in the HTTP module at https://nodejs.org/api/http.html#http_class_http_serverresponse . But for most common use, the previous methods should suffice.
Express is a web framework that has minimal functionality of its own. An Express application is essentially a series of middleware function calls. In fact, the Router itself is nothing but a middleware function. The distinction is that middleware usually works on generic handling of requests and/or things that need to be done for all or most requests, but not necessarily be the last in the chain, that sends out a response. A route, on the other hand, is meant to be used for a specific path+method combination and is expected to send out a response.
Middleware functions are those that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next. I won’t go into the details of how to write your own middleware function, since we will not be writing new middleware in the application. But we will use some middleware for sure, so it’s handy to understand how any middleware works at a high level.
We already used one middleware called express.static in the Hello World example, to serve static files. This is the only built-in middleware (other than the router) available as part of Express. But there are other very useful middleware supported by the Express team, of which we will be using body-parser in this chapter, though indirectly. Third-party middleware is available via npm.
In the case of the static middleware, we constructed a middleware function by calling express.static() method. This not only returned a middleware function, but also configured it to use the directory called public to look for the static files.
This would have mounted the static middleware on the path /public and all static files would have to be accessed with the prefix /public, for example, /public/index.html.
REST (short for representational state transfer) is an architectural pattern for application programming interfaces (APIs). There are other older patterns such as SOAP and XMLRPC, but of late, the REST pattern has gained popularity.
Since the APIs in the Issue Tracker application are only for internal consumption, we could use any API pattern or even invent our own. But let’s not do that because using an existing pattern forces you to think and organize the APIs and schema better and encourages some good practices.
Although we won’t be using the REST pattern, I’ll discuss it briefly since it is one of the more popular choices due to its simplicity and small number of constructs. It’ll let you appreciate the differences and the logic for the choice that I’ll make eventually, to use GraphQL.
The APIs are resource based (as opposed to action based). Thus, API names like getSomething or saveSomething are not normal in REST APIs. In fact, there are no API names in the conventional sense, because APIs are formed by a combination of resources and actions. There are really only resource names called endpoints.
Resources are accessed based on a Uniform Resource Identifier (URI), also known as an endpoint. Resources are nouns (not verbs). You typically use two URIs per resource: one for the collection (like /customers) and one for an individual object (like /customers/1234), where 1234 uniquely identifies a customer.
Resources can also form a hierarchy. For example, the collection of orders of a customer is identified by /customers/1234/orders, and an order of that customer is identified by /customers/1234/orders/43.
Operation | Method | Resource | Example | Remarks |
|---|---|---|---|---|
Read – List | GET | Collection | GET /customers | Lists objects (additional query string can be used for filtering and sorting) |
Read | GET | Object | GET /customers/1234 | Returns a single object (query string may be used to specify which fields) |
Create | POST | Collection | POST /customers | Creates an object with the values specified in the body |
Update | PUT | Object | PUT /customers/1234 | Replaces the object with the one specified in the body |
Update | PATCH | Object | PATCH /customers/1234 | Modifies some properties of the object, as specified in the body |
Delete | DELETE | Object | DELETE /customers/1234 | Deletes the object |
Some other operations such as DELETE and PUT in the collection may also be used to delete and modify the entire collection in one shot, but this is not common usage. HEAD and OPTIONS are also valid verbs that give out information about the resources rather than actual data. They are used mainly for APIs that are externally exposed and consumed by many different clients.
Filtering, sorting, and paginating a list of objects. The query string is commonly used in an implementation-specific way to specify these.
Specifying which fields to return in a READ operation.
If there are embedded objects, specifying which of those to expand in a READ operation.
Specifying which fields to modify in a PATCH operation.
Representation of objects. You are free to use JSON, XML, or any other representation for the objects in both READ and WRITE operations.
Given the fact that different API sets use different ways of dealing with these issues, most REST API implementations are more REST-like than strict REST. This has affected common adoption and there are, therefore, a lack of tools that can help do much of the common things that need to be done to implement a REST-based API.
Although the REST paradigm is quite useful in making APIs predictable, the shortcomings discussed previously have made it hard to use it when different clients access the same set of APIs. For example, how an object is displayed in a mobile application and the same is displayed in a desktop browser can be quite different, and therefore, a more granular control as well as aggregation of different resources may work better.
GraphQL was developed to address just these concerns. As a result, GraphQL is a far more elaborate specification, with the following salient features.
Unlike REST APIs, where you have little control on what the server returns as part of an object, in GraphQL, the properties of an object that need to be returned must be specified. Specifying no fields of an object would, in a REST API, return the entire object. In contrast, in a GraphQL query, it is invalid to request nothing.
This lets the client control the amount of data that is transferred over the network, making it more efficient, especially for lighter front-ends such as mobile applications. Further, addition of new capabilities (fields or new APIs) does not require you to introduce a new version of the API set. Given a query, since the shape of the returned data is determined by it, the effect of it is the same, regardless of changes to the API.
A downside to this that there is a bit of a learning curve for the GraphQL query language, which must be used to make any API call. Fortunately, the specification of the language is quite simple and easy to master.
REST APIs were resource based, whereas GraphQL is graph based. This means that relationships between objects are naturally handled in GraphQL APIs.
In the Issue Tracker application, you could think of Issues and Users having a relation: An issue is assigned to a user, and a user has one or more issues assigned to them. When querying for a user’s properties, GraphQL makes it natural to query for some properties associated with all the issues assigned to them as well.
GraphQL API servers have a single endpoint in contrast to one endpoint per resource in REST. The name of the resource(s) or field(s) being accessed is supplied as part of the query itself.
This makes it possible to use a single query for all the data that is required by a client. Due to the graph-based nature of the query, all related objects can be retrieved as part of a query for one object. Not only that, even unrelated objects can be queried in a single call to the API server. This obviates the need for "aggregation" services whose job was to put together multiple API results into one bundle.
GraphQL is a strongly typed query language. All fields and arguments have a type against which both queries and results can be validated and give descriptive error messages. In addition to types, it is also possible to specify which fields and arguments are required and which others are optional. All this is done using the GraphQL schema language.
The advantage of a strongly typed system is that it prevents errors. This is a great thing, considering that APIs are written and consumed by different teams and there is bound to be communication gaps due to this.
The type system of GraphQL has its own language for specifying the details of the types that you wish to support in your API. It supports the basic scalar types such as integer and string, objects composed of these basic data types, and custom scalar types and enumerations.
A GraphQL server can be queried for the types it supports. This creates a powerful platform for tools and client software to build atop this information. This includes code-generation utilities in statically typed languages and explorers that let developers test and learn an API set quickly, without grepping the codebase or wrangling with cURL.
We will be using one such tool, called the Apollo Playground, to test our APIs before integrating them into the application’s UI.
Parsing and dealing with the type system language (also called the GraphQL Schema Language) as well as the query language is hard to do on your own. Fortunately, there are tools and libraries available in most languages for this purpose.
For JavaScript on the back-end, there is a reference implementation of GraphQL called GraphQL.js. To tie this to Express and enable HTTP requests to be the transport mechanism for the API calls, there is a package called express-graphql.
But these are very basic tools that lack some advanced support such as modularized schemas and seamless handling of custom scalar types. The package graphql-tools and the related apollo-server are built on top of GraphQL.js to add these advanced features. We will be using the advanced packages for the Issue Tracker application in this chapter.
I will cover only those features of GraphQL that are needed for the purpose of the application. For advanced features that you may need in your own specific application, do refer to the complete documentation of GraphQL at https://graphql.org and the tools at https://www.apollographql.com/docs/graphql-tools/ .
Let’s start with a simple API that returns a string, called About. In this section, we’ll implement this API as well as another API that lets us change the string that is returned by this API. This will let you learn the basics of simple reads as well as writes using GraphQL.
For the About API, we don’t need any special types, just the basic data type String is good enough. But GraphQL schema has two special types that are entry points into the type system, called Query and Mutation. All other APIs or fields are defined hierarchically under these two types, which are like the entry points into the API. Query fields are expected to return existing state, whereas mutation fields are expected to change something in the application’s data.
A schema must have at least the Query type. The distinction between the query and mutation types is notional: there is nothing that you can do in a query or mutation that you cannot do in the other. But a subtle difference is that whereas query fields are executed in parallel, mutation fields are executed in series. So, it’s best to use them as they are meant to be used: implement READ operations under Query and things that modify the system under Mutation.
Int: A signed 32-bit integer.
Float: A signed double-precision floating-point value.
String: A UTF-8 character sequence.
Boolean: true or false.
ID: This represents a unique identifier, serialized as a string. Using an ID instead of a string indicates that it is not intended to be human-readable.
In addition to specifying the type, the Schema Language has a provision to indicate whether the value is optional or mandatory. By default, all values are optional (i.e., they can be null), and those that require a value are defined by adding an exclamation character (!) after the type.
Note that all arguments must be named. There are no positional arguments in the GraphQL Schema Language. Also, all fields must have a type, and there is no void or other type that indicates that the field returns nothing. To overcome this, we can just use any data type and make it optional so that the caller does not expect a value.
Note that I stopped calling these APIs, rather I am calling even something like setAboutMessage a field. That’s because all of GraphQL has only fields, and accessing a field can have a side effect such as setting some value.
The next step is to have handlers or functions that can be called when these fields are accessed. Such functions are called resolvers because they resolve a query to a field with real values. Although the schema definition was done in the special Schema Language, the implementation of resolvers depends on the programming language that we use. For example, if you were to define the About API set, say, in Python, the schema string would look the same as in JavaScript. But the handlers would look quite different from what we are going to write in JavaScript.
obj: The object that contains the result returned from the resolver on the parent field. This argument enables the nested nature of GraphQL queries.
args: An object with the arguments passed into the field in the query. For example, if the field was called with setAboutMessage(message: "New Message"), the args object would be: { "message": "New Message" }.
context: This is an object shared by all resolvers in a particular query and is used to contain per-request state, including authentication information, dataloader instances, and anything else that should be taken into account when resolving the query.
info: This argument should only be used in advanced cases, but it contains information about the execution state of the query.
We used the ES2015 Destructuring Assignment feature to access the message property present inside the second argument called args. This is equivalent to naming the argument as args and accessing the property as args.message rather than simply message.
We used the ES2015 Object Property Shorthand to specify the value of the setAboutMessage property. When the property name and the variable name assigned to it are the same, the variable name can be skipped. Thus, { setAboutMessage: setAboutMessage } can be simply written as { setAboutMessage }.
Although no effort has been spared to ensure that all code listings are accurate, there may be typos, formatting errors such as type of quotes, or even corrections that did not make it to the book before it went to the press. So, always rely on the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ) as the tested and up-to-date source for all code listings, especially if something does not work as expected.
As I pointed out earlier, GraphQL schema and introspection allows tools to be built that can let developers explore the API. The tool called Playground is available by default as part of the Apollo Server and can be accessed simply by browsing the API endpoint. Thus, if you type http://localhost:3000/graphql in your browser’s URL bar, you’ll find the Playground UI.

The GraphQL Playground
Before we test the APIs, it’s a good idea to explore the schema using the green SCHEMA button on the right side of the UI. On doing that, you’ll find that the about and setAboutMessage fields are described in the schema. To make a query, you can type the query in the left-side window and see the results on the right side after clicking on the Play button, as described in the UI.
Note that there is an autocompletion feature in the Playground, which may come in handy as you type. The Playground also shows errors in the query using red underlines. These features use the schema to know about the available fields, arguments, and their types. The Playground queries the schema from the server, so whenever the schema changes, if you rely on autocompletion, you need to refresh the browser so that the changed schema is retrieved from the server.
The output is a regular JSON object unlike the query, which followed the query language syntax. It also reflects the structure of the query, with "data" as the root object in the result.
Now, running the original about query (in the first tab) should return the new message, "Hello World!" to prove that the new message has been successfully set in the server. To assure ourselves that there is no magic that the Playground is doing, let’s also run a query using cURL on the command line for the about field.
Use the same URL in the browser as well as command line for cURL. For example, type curl http://localhost:3000/graphql, which is the same URL as we used in the browser to invoke the Playground. Or, copy paste the curl command we used for doing a GET request for the about field. What do you see? Can you explain the difference? Hint: Compare the request headers.
What are the pros and cons of using GET vs. POST for read-only API calls?
Answers are available at the end of the chapter.
In the previous section, we specified the GraphQL schema within the JavaScript file. If and when the schema grows bigger, it would be useful to separate the schema into a file of its own. This will help keep the JavaScript source files smaller, and IDEs may be able to format these files and enable syntax coloring.
There’s just one other thing that needs a change: the nodemon tool that restarts the server on detecting changes to files by default only looks for changes to files with a .js extension. To make it watch for changes to other extensions, we need to add an -e option specifying all the extensions it needs to watch for. Since we added a file with extension .graphql, let’s specify js and graphql as the two extensions for this option.
If you restart the server using npm start now , you will be able to test the APIs using the Playground and ensure that they behave as before.
Now that you have learned the basics of GraphQL, let’s make some progress toward building the Issue Tracker application using this knowledge. The next thing we’ll do is implement an API to fetch a list of issues. We’ll test it using the Playground and, in the next section, we’ll change the front-end to integrate with this new API.
Now, let’s add a new field under Query to return a list of issues. The GraphQL way to specify a list of another type is to enclose it within square brackets. We could use [Issue] as the type for the field, which we will call issueList. But we need to say not only that the return value is mandatory, but also that each element in the list cannot be null. So, we have to add the exclamation mark after Issue as well as after the array type, as in [Issue!]!.
In the server code, we need to add a resolver under Query for the new field, which points to a function. We’ll also have an array of issues (a copy of what we have in the front-end code) that is a stand-in for a database. We can immediately return this array from the resolver. The function could be in-place like that for the about field, but knowing that we’ll expand this function to do more than just return a hard-coded array, let’s create a separate function called issueList for it.
To test this in the Playground, you will need to run a query that specifies the issueList field, with subfields. But first, a refresh of the browser is needed so that the Playground has the latest schema and doesn’t show errors when you type the query.
The array itself need not be expanded in the query. It is implicit (due to the schema specification) that issueList returns an array, and therefore, subfields of the field are automatically expanded within the array.
If you add more subfields in the query, their values will also be returned. If you look at the date fields, you will see that they have been converted from a Date object to a string using the toString() method of the Date JavaScript object.
Try to specify no subfields for the issueList field, like query { issueList }, like we did for the about field and click the Play button. What do you observe as the result? Try to specify an empty field list using query { issueList { } } instead and play the request. What do you see now? Can you explain the difference?
Add an invalid subfield (say, test) to the query under issueList. What error do you expect when you click the Play button? In particular, would Playground send the request to the server? Try it out in the Playground, with the Developer Console open.
How would an aggregated query look, one that includes the list of issues as well as the about field?
Answers are available at the end of the chapter.
Now that we have the List API working, let’s get it integrated into the UI. In this section, we will replace the implementation of the loadData() method in the IssueList React component with something that fetches the data from the server.
The polyfill is required only for Internet Explorer and older versions of other browsers. All latest versions of popular browsers—such as Chrome, Firefox, Safari, Edge, and Opera—support fetch() natively.
We used the await keyword to deal with asynchronous calls. This is part of the ES2017 specification and is supported by the latest versions of all browsers except Internet Explorer. It is automatically handled by Babel transforms for older browsers. Also, await can only be used in functions that are marked async. We will have to add the async keyword to the loadData() function soon. If you are not familiar with the async/await construct, you can learn about it at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function .
We will also need to add the keyword async for the function definition of loadData() since we have used awaits within this function.

After List API integration
Storing dates as strings may seem to work most times, but not always. For one, sorting and filtering on dates makes it harder, because one has to convert from string to Date types every time. Also, dates should ideally be displayed in the user’s time zone and locale, regardless of where the server is. Different users may see the same dates differently based on where they are, and even see dates in the form of "2 days ago" etc.
To achieve all that, we need to store dates as JavaScript’s native Date objects. It should ideally be converted to a locale-specific string at the time of displaying it to the user only. But unfortunately, JSON does not have a Date type, and thus, transferring data using JSON in API calls also must convert the date to and from strings.
The recommended string format for transferring Date objects in a JSON is the ISO 8601 format. It is concise and widely accepted. It is also the same format used by JavaScript Date’s toJSON() method. In this format, a date such as 26 January 2019, 2:30 PM UTC would be written as 2019-01-26T14:30:00.000Z. It is easy and unambiguous to convert a date to this string using either the toJSON() or the toISOString() methods of Date, as well as to convert it back to a date using new Date(dateString).
Define a type for the scalar using the scalar keyword instead of the type keyword in the schema.
Add a top-level resolver for all scalar types, which handles both serialization (on the way out) as well as parsing (on the way in) via class methods.
Two other methods, parseValue() and parseLiteral(), are needed to parse strings back to dates. Let’s leave this parsing to a later stage when it is really needed for accepting input values, since these are optional methods.
Now, in App.jsx, we can convert the string to the native Date type. One way to do this is to loop through the issues after fetching them from the server and replace the fields due and created with their date equivalents. A better way do this is to pass a reviver function to the JSON parse() function. A reviver function is one that is called for parsing all values, and the JSON parser gives it a chance to modify what the default parser would do.
With this set of changes, the application should appear as before, at the end of the previous chapter. The dates will look nicely formatted. Even adding an issue should work, but on refreshing the browser, the added issue will disappear. That’s because we have not saved the issue in the server—all we have done is changed the local state of the issue list in the browser, which will be reset to the initial set of issues on a refresh.
In server.js, remove the resolver that associates the type GraphQLDate to the resolver object. Make the API request for issueList to be called. Is there any difference in the output? What do you think could explain the difference or lack of difference?
How can you be sure that the scalar type resolver is indeed being used?
Answers are available at the end of the chapter.
In this section, we will implement an API for creating a new issue in the server, which will be appended to the list of issues in the server’s memory.
To do this, we have to first define a field in the schema under Mutation called issueAdd. This field should take arguments, just as the setAboutMessage field did. But this time, we need multiple arguments, one for each property of the issue being added. Alternatively, we can define a new type as an object that has the fields we need for the input. This can’t be the same as the Issue type because it has some required fields (id and created) that are not part of the input. These are values that are set by the server only. Further, GraphQL needs a different specification when it comes to input types. Instead of using the type keyword, we have to use the input keyword.
Now, we can use the type IssueInputs as the argument type to the new issueAdd field under Mutation. The return value of this field can be anything. It is usually good practice to return the values generated at the server, typically the ID of the new object. In this case, since both the ID and the created date are set at the server, let’s return the entire issue object that was created.
We had postponed implementing the parsers for the custom scalar type GraphQLDate because we didn’t need it then. But now, since the type IssueInputs does have a GraphQLDate type, we must implement the parsers for receiving date values. There are two methods that need to be implemented in the GraphQLDate resolver: parseValue and parseLiteral.
A return value of undefined indicates to the GraphQL library that the type could not be converted, and it will be treated as an error.

Schema showing descriptions of IssueInputs and status
This shows that the due date has been properly parsed and converted. The status field also has been defaulted to 'New' as expected. You can also confirm that the issue has been created by running a query for issueList in the Playground and checking the results.
We used an input complex type to supply the values for issueAdd. Compare this with passing each field individually, like issueAdd(title: String!, owner: String ...). What are the pros and cons of each method?
Instead of a valid date string, try passing a valid integer like due: 2018 for the field. What do you think the value of ast.kind will be in parseLiteral? Add a console.log message in parseLiteral and confirm this. What other values of ast.kind do you think are possible?
Pass a string, but an invalid date, like "abcdef" for the due field. What happens? How can one fix this?
Is there another way of specifying default values for the status field? Hint: Read up on passing arguments in the GraphQL schema documentation at http://graphql.github.io/learn/schema/#arguments .
Answers are available at the end of the chapter.
Before making the API call, we need a query with the values of the fields filled in. Let’s use a template string to generate such a query within the createIssue() method in IssueList. We can use the title and owner properties of the passed-in issue object as they are, but for the date field due, we have to explicitly convert this to a string as per the ISO format, because that’s the format we decided for passing dates.
On testing this set of changes by adding a new issue using the UI, you will find that the due date is set to 10 days from the current date. Also, if you refresh the browser, you will find that the added issue stays, because the new issue has now been saved on the server.
Add a new issue with a title that has quotes in it, for example, Unable to create issue with status "New". What happens? Inspect the console as well as the request and response in the Developer Console of the browser. How do you think this can be fixed?
Answers are available at the end of the chapter.
For both the mutation calls, we have specified the arguments to the fields inside the query string. When trying out an API in the Playground, as we did for setAboutMessage, this works great. But in most applications, the arguments will be dynamic, based on user input. And that’s exactly what happened with issueAdd, and we had to construct the query string using a string template.
This isn’t a great idea, firstly because of the small overhead of converting a template to the actual string. A more important reason is that special characters such as quotes and curly braces will need to be escaped. This is quite error-prone, and easy to miss. Since we have not done any escaping, if you test the Issue Tracker application at this point in time by adding an issue that has a double quote in say, the title, you will find that it doesn’t work correctly.
GraphQL has a first-class way to factor dynamic values out of the query and pass them as a separate dictionary. These values are called variables. This way of passing dynamic values is quite similar to prepared statements in SQL queries.
Now, to supply the value of the variable, we need to send it across in a JSON object that is separate from the query string. In the Playground, there is a tab called QUERY VARIABLES in the bottom-right corner. Clicking on this will split the request window and allow you to type in the query variables in the bottom half. We need to send across the variables as a JSON object with the name of the variable (without the $) as a property and its value as the property’s value.

Playground with query variables
If you inspect the request data in the Developer Console, you will find that the request JSON has three properties—operationName, variables, and query. While we were using only the query until now, to take advantage of variables, we’ve had to use the other two as well.
GraphQL specification allows multiple operations to be present in the same query string. But only one of these can be executed in one call. The value of operationName specifies which of those operations needs to be executed.
On testing these changes in the Issue Tracker application, you will find that adding a new issue works as before. Further, you should be able to use double quotes in the title of a newly added issue without causing any errors.
In the custom scalar type GraphQLDate, now that we are using variables, which one of the parsing methods do you think will be called? Will it be parseLiteral or parseValue? Add a temporary console.log statement in both these functions and confirm your answer.
Answers are available at the end of the chapter.
We have kind of ignored validation up until now. But all applications have some typical validations required, not just to prevent invalid input from the UI, but also to prevent invalid inputs from direct API calls. In this section we will add a few validations that are typical for most applications.
Note that returning undefined is treated as an error by the library. If the supplied literal is not a string, the function will not return anything, which is the same as returning undefined.
Finally, if you remove the status altogether, you will find that it does default the value to New as seen in the result window.
In this section, we’ll modify the user interface to show any error messages to the user. We’ll deal with transport errors due to network connection problems as well as invalid user input. Server and other errors should normally not occur for the user (these would most likely be bugs), and if they do, let’s just display the code and the message as we receive them.
We used the ES2015 Default Function parameter to assign {} to the parameter variables in case it was not passed in by the caller. Read more about this feature at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters .

Transport error message
As for the other messages, the length of title can be tested by typing a small title in the user input. The other validations have to be tested only by temporarily changing the code, for example, by setting the status to the desired value and setting the due field to an invalid date string etc. within the IssueAdd component’s handleSubmit() method.
In this chapter, we compared two API standards: REST and GraphQL. Although REST is widely used, we chose GraphQL for the features and ease of implementation, given that there are tools and libraries to help us build the APIs.
GraphQL is an extremely structured API standard and is quite extensive. I covered only the basics of GraphQL, which only included the features that were required for the Issue Tracker application at this stage. I encourage you to read more on GraphQL at https://graphql.org/ . There are advanced features such as directives and fragments, which can be helpful in reusing code for building queries. These can be quite handy in a larger project, but I will not cover these as part of this book as they are not really required for the Issue Tracker application.
Using GraphQL, you saw how to build the C and R part of CRUD in this chapter. You also saw how easy some of the validations were to implement and how the strong type system of GraphQL helps avoid errors and makes the APIs self-documenting. We will deal with U and D part of CRUD in later chapters, as and when we build those features.
In the meantime, it would be a good idea to see how to persist the data. We moved the array of issues from the browser’s memory to the server’s memory. In the next chapter, we’ll move it further, to a real database, MongoDB.
The same URL in the browser and the cURL command line cause different results. In the browser, the Playground UI is returned, whereas in the command line, the API is executed. The Apollo Server makes the distinction by looking at the Accept header. If it finds "text/html" (which is what browsers send), it returns the Playground UI. You can check this out by adding --header "Accept: text/html" to the cURL command line and executing it.
The browser can cache GET requests and return the response from the cache itself. Different browsers behave differently and it’s hard to predict the right behavior. Normally, you would want API results to never be cached, instead always fetched from the server. In such cases, using POST is safe, since browsers don’t cache the results of a POST.
But in case you really want the browser to cache certain API responses where possible, because the result is large and doesn’t change (e.g., images), GET is the only option. Or, you could use POST but deal with caching yourself (e.g., by using local storage) rather than let the browser handle it.
In the first case, the query has a valid syntax, but it does not conform to the schema. The Playground sent the request, and the server responded with an error to that effect.
In the second case, the Playground did not send the query to the server (you can see an error using the Developer Console in the console logs), because it found that the query does not have a valid syntax: a subfield name is expected within the curly braces.
In both cases, the Playground does show the errors as red underlines in the query. Hovering the cursor on the red underlines will show the actual error message regardless of whether it is a syntax error or a schema error.
Adding an invalid subfield does not make the query syntactically invalid. The request is sent to the server, which validates the query and returns an error saying that the subfield is invalid.
The query can be like query { about issueList { id title created } }. In the result, you can see that both about and issueList are returned as properties of the data object.
The output is identical to the case whether or not the resolver for the scalar type is being used. The fact that the schema defined GraphQLDate as a scalar type has made the default resolver for the Date object use toJSON() instead of toString().
A console.log() message could be added in the serialize function. Alternatively, if you temporarily change the conversion to use Date.toString() instead of Date.toISOString(), you can see that the conversion is being done differently.
In terms of verbosity, both methods are the same, all the common properties have to be repeated between Issue and IssueInputs or the argument list. If the list of properties change, for example, if we add a new field called severity, the change has to be made in two places: in the Issue type and in the IssueInputs type or the argument list to issueAdd.
One advantage of defining an input type is that the same type can be reused. This can come in handy if, for example, both the Create and the Update operations can take in the same input type.
Passing an integer will set ast.kind to Kind.INT (which is set to the string 'IntValue', as seen in the console log). Other possible values are Kind.FLOAT, Kind.BOOLEAN, and Kind.ENUM.
Passing a valid string but an invalid date will not throw any errors during the creation of the issue, but the issue will be saved with an invalid Date object, which is the result of new Date() using an invalid date string. The effect of this will be seen when an issue is returned; there will be errors that show that the date object cannot be converted to a string. We will add validations later in this chapter.
A default value can be specified in the schema by adding an = symbol and the default value after the type specification, like status: String = "New". We will switch to this method later in this chapter.
The issue is not created, and the console will have an error indicating a bad request. You will find that the request is malformed because the quote ends the string, which is the value for the title, and everything after that is unrecognized by the GraphQL query parser.
One way to remedy this is to look for quotes within string values and escape them using a backslash (\) character. But as you will see in the next section, there is a better way to do this.
Since the values are not being passed as literals within the query string, it is parseValue that will be called now.
In this chapter, I’ll take up MongoDB, the database layer and the M in the MERN stack. Until now, we had an array of issues in the Express server’s memory that we used as the database. We’ll replace this with real persistence and read and write the list of issues from a MongoDB database.
To achieve this, we’ll need to install or use MongoDB on the cloud, get used to its shell commands, install a Node.js driver to access it from Node.js, and finally modify the server code to replace the API calls to read and write from a MongoDB database instead of the in-memory array of issues.
This is an introductory section, where we will not be modifying the application. We’ll look at these core concepts in this section: MongoDB, documents, and collections. Then, we’ll set up MongoDB and explore these concepts with examples using the mongo shell to read and write to the database.
MongoDB is a document database, which means that the equivalent of a record is a document, or an object. In a relational database, you organize data in terms of rows and columns, whereas in a document database, an entire object can be written as a document.
For simple objects, this may seem no different from a relational database. But let’s say you have objects with nested objects (called embedded documents) and arrays. Now, when using a relational database, this will typically need multiple tables. For example, in a relational database, an Invoice object may be stored in a combination of an invoice table (to store the invoice details such as the customer address and delivery details) and an invoice_items table (to store the details of each item that is part of the shipment). In MongoDB, the entire Invoice object would be stored as one document. That’s because a document can contain arrays and other objects in a nested manner and the contained objects don’t have to be separated out into other documents.
A document is a data structure composed of field and value pairs. The values of fields may include objects, arrays, and arrays of objects and so on, as deeply nested as you want it to be. MongoDB documents are similar to JSON objects, so it is easy to think of them as JavaScript objects. Compared to a JSON object, a MongoDB document has support not only for the primitive data types—Boolean, numbers, and strings—but also other common data types such as dates, timestamps, regular expressions, and binary data.
In this document, there are numbers, strings, and a date data type. Further, there is a nested object (billingAddress) and an array of objects (items).
A collection is like a table in a relational database: it is a set of documents. Just like in a relational database, the collection can have a primary key and indexes. But there are a few differences compared to a relational database.
A primary key is mandated in MongoDB, and it has the reserved field name _id. Even if _id field is not supplied when creating a document, MongoDB creates this field and auto-generates a unique key for every document. More often than not, the auto-generated ID can be used as is, since it is convenient and guaranteed to produce unique keys even when multiple clients are writing to the database simultaneously. MongoDB uses a special data type called the ObjectId for the primary key.
The _id field is automatically indexed. Apart from this, indexes can be created on other fields, and this includes fields within embedded documents and array fields. Indexes are used to efficiently access a subset of documents in a collection.
Unlike a relational database, MongoDB does not require you to define a schema for a collection. The only requirement is that all documents in a collection must have a unique _id, but the actual documents may have completely different fields. In practice, though, all documents in a collection do have the same fields. Although a flexible schema may seem very convenient for schema changes during the initial stages of an application, this can cause problems if some kind of schema checking is not added in the application code.
As of version 3.6, MongoDB has supported a concept of schema, even though it is optional. You can read all about MongoDB schemas at https://docs.mongodb.com/manual/core/schema-validation/index.html . A schema can enforce allowed and required fields and their data types, just like GraphQL can. But it can also validate other things like string length and minimum and maximum values for integers.
But the errors generated because of schema violations do not give enough details as to which of the validation checks fail as of version 3.6. This may improve in future versions of MongoDB, at which point in time it is worth considering adding full-fledged schema checks. For the Issue Tracker application, we’ll not use the schema validation feature of MongoDB, instead, we’ll implement all necessary validations in the back-end code.
A database is a logical grouping of many collections. Since there are no foreign keys like in a SQL database, the concept of a database is nothing but a logical partitioning namespace. Most database operations read or write from a single collection, but $lookup, which is a stage in an aggregation pipeline, is equivalent to a join in SQL databases. This stage can combine documents within the same database.
Further, taking backups and other administrative tasks work on the database as a unit. A database connection is restricted to accessing only one database, so to access multiple databases, multiple connections are required. Thus, it is useful to keep all the collections of an application in one database, though a database server can host multiple databases.
Unlike the universal English-like SQL in a relational database, the MongoDB query language is made up of methods to achieve various operations. The main methods for read and write operations are the CRUD methods. Other methods include aggregation, text search, and geospatial queries.
All methods operate on a collection and take parameters as JavaScript objects that specify the details of the operation. Each method has its own specification. For example, to insert a document, the only argument needed is the document itself. For querying, the parameters are a query filter and a list of fields to return (also called the projection).
Since there is no "language" for querying or updating, the query filters can be very easily constructed programmatically.
Unlike relational databases, MongoDB encourages denormalization, that is, storing related parts of a document as embedded subdocuments rather than as separate collections (tables) in a relational database. Take an example of people (name, gender, etc.) and their contact information (primary address, secondary address etc.). In a relational database, this would require separate tables for People and Contacts, and then a join on the two tables when all of the information is needed together. In MongoDB, on the other hand, it can be stored as a list of contacts within the same People document. That’s because a join of collections is not natural to most methods in MongoDB: the most convenient find() method can operate only on one collection at a time.
MongoDB Atlas ( https://www.mongodb.com/cloud/atlas ): I refer to this as Atlas for short. A small database (shared RAM, 512 MB storage) is available for free.
mLab (previously MongoLab) ( https://mlab.com/ ): mLab has announced an acquisition by MongoDB Inc. and may eventually be merged into Atlas itself. A sandbox environment is available for free, limited to 500 MB storage.
Compose ( https://www.compose.com ): Among many other services, Compose offers MongoDB as a service. A 30-day trial period is available, but a permanently free sandbox kind of option is not available.
Of these three, I find Atlas the most convenient because there are many options for the location of the host. When connecting to the database, it lets me choose one closest to my location, and that minimizes the latency. mLab does not give a cluster—a database can be created individually. Compose is not permanently free, and it is likely that you may need more than 30 days to complete this book.
The downside of any of the hosted options is that, apart from the small extra latency when accessing the database, you need an Internet connection. Which means that you may not be able to test your code where Internet access is not available, for example, on a flight. In comparison, installing MongoDB on your computer may work better, but the installation takes a bit more work than signing up for one of the cloud-based options.
Even when using one of the cloud options, you will need to download and install the mongo shell to be able to access the database remotely. Each of the services come with instructions on this step as well. Choose version 3.6 or higher of MongoDB when signing up for any of these services. Test the signup by connecting to the cluster or database using the mongo shell, by following instructions given by the service provider.
If you choose to install MongoDB on your computer (it can be installed easily on OS X, Windows, and most distributions based on Linux), look up the installation instructions, which are different for each operating system. You may install MongoDB by following the instructions at the MongoDB website ( https://docs.mongodb.com/manual/installation/ or search for “mongodb installation” in your search engine).
Choose MongoDB version 3.6 or higher, preferably the latest, as some of the examples use features introduced only in version 3.6. Most local installation options let you install the server, the shell, and tools all in one. Check that this is the case; if not, you may have to install them separately.
The message you see can be slightly different from this, especially if you have installed a different version of MongoDB. But you do need to see the prompt > where you can type further commands. If, instead, you see an error message, revisit the installation and the server starting procedure.
The mongo shell is an interactive JavaScript shell, very much like the Node.js shell. In the interactive shell, a few non-JavaScript conveniences are available over and above the full power of JavaScript. In this section, we’ll discuss the basic operations that are possible via the shell, those that are most commonly used. For a full reference of all the capabilities of the shell, you can take a look at the mongo shell documentation at https://docs.mongodb.com/manual/mongo/ .
The commands that we will be typing in the mongo shell have been collected together in a file called mongo_commands.txt. These commands have been tested to work as is on Atlas or a local installation, but you may find variations in the other options. For example, mLab lets you connect only to a database (as opposed to a cluster), so it does not allow of switching between databases in mLab.
If you find that something is not working as expected when typing a command, cross-check the commands with the same in the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ). This is because typos may have been introduced during the production of the book, or last-minute corrections may have missed making it to the book. The GitHub repository, on the other hand, reflects the most up-to-date and tested set of code and commands.
You will find that there are no collections in this database, since it is a fresh installation. Further, you will also find that the database test was not listed when we listed the available databases. That’s because databases and collections are really created only on the first write operation to any of these.
This is the auto-completion feature of the mongo shell at work. Note that you can let the mongo shell auto-complete the name of any method by pressing the Tab character after entering the beginning few characters of the method.
The shell by itself does very little apart from providing a mechanism to access methods of the database and collections. It is the JavaScript engine, which forms the basis of the shell and gives a lot of flexibility and power to the shell.
In the next section, we will discuss more methods on the collection, such as insertOne() that you just learned about. These methods are accessible from many programming languages via a driver. The mongo shell is just another tool that can access these methods. You will find that the methods and arguments available in other programming languages are very similar to those in the mongo shell.
Using the shell, display a list of methods available on the cursor object. Hint: Look up the mongo shell documentation for mongo Shell Help at https://docs.mongodb.com/manual/tutorial/access-mongo-shell-help/ .
Answers are available at the end of the chapter.
This is different from removing all the documents in the collection, because it also removes any indexes that are part of the collection.
This works just fine, and using find() , you can see that two documents exist in the collection, but they are not necessarily the same schema. This is the advantage of a flexible schema: the schema can be enhanced whenever a new data element that needs to be stored is discovered, without having to explicitly modify the schema.
In this case, it is implicit that any employee document where the middle field under name is missing indicates an employee without a middle name. If, on the other hand, a field was added that didn’t have an implicit meaning when absent, its absence would have to be handled in the code. Or a migration script would have to be run that defaults the field’s value to something.
You will also find that the format of the _id field is different for the two documents, and even the data type is different. For the first document, the data type is an integer. For the second, it is of type ObjectID (which is why it is shown as ObjectID(...). Thus, it’s not just the presence of fields that can differ between two documents in the same collection, even the data types of the same field can be different.
In most cases, leaving the creation of the primary key to MongoDB works just great, because you don’t have to worry about keeping it unique: MongoDB does that automatically. But, this identifier is not human-readable. In the Issue Tracker application, we want the identifier to be a number so that it can be easily remembered and talked about. But instead of using the _id field to store the human-readable identifier, let’s use a new field called id and let MongoDB auto-generate _id.
Now that there are multiple documents in the collection, let’s see how to retrieve a subset of the documents as opposed to the full list. The find() method takes in two more arguments. The first is a filter to apply to the list, and the second is a projection, a specification of which fields to retrieve.
Note that we did not use pretty() here, yet, the output is prettified. This is because findOne() returns a single object and the mongo shell prettifies objects by default.
The number of documents returned now should be reduced to only one, since there is only one document that matched both the criteria, the last name being equal to 'Doe' as well as age being greater than 30. Note that we used the dot notation for specifying a field embedded in a nested document. And this also made us use quotes around the field name, since it is a regular JavaScript object property.
To match multiple values of the same field—for example, to match age being greater than 30 and age being less than 60—the same strategy cannot be used. That’s because the filter is a regular JavaScript object, and two properties of the same name cannot exist in a document. Thus, a filter like { age: { $gte: 30 }, age: { $lte: 60 } } will not work (JavaScript will not throw an error, instead, it will pick just one of the values for the property age). An explicit $and operator has to be used, which takes in an array of objects specifying multiple field-value criteria. You can read all about the $and operator and many more operators in the operators section of the reference manual of MongoDB at https://docs.mongodb.com/manual/reference/operator/query/ .
With this index, any query that uses a filter that has the field age in it will be significantly faster because MongoDB will use this index instead of scanning through all documents in the collection. But this was not a unique index, as many people can be the same age.
All this while, we retrieved the entire document that matched the filter. In the previous section, when we had to print only a subset of the fields of the document, we did it using a forEach() loop. But this means that the entire document is fetched from the server even when we needed only some parts of it for printing. When the documents are large, this can use up a lot of network bandwidth. To restrict the fetch to only some fields, the find() method takes a second argument called the projection. A projection specifies which fields to include or exclude in the result.
There are two methods—updateOne() and updateMany()—available for modifying a document. The arguments to both methods are the same, except that updateOne() stops after finding and updating the first matching document. The first argument is a query filter, the same as the filter that find() takes. The second argument is an update specification if only some fields of the object need to be changed.
The matchedCount returned how many documents matched the filter. If the filter had matched more than one, that number would have been returned. But since the method is supposed to modify only one document, the modified count should always be 1, unless the modification had no effect. If you run the command again, you will find that modifiedCount will be 0, since the age was already 23 for the employee with ID 2.
Note that even though the field organization did not exist in the documents, the new value MyCompany would have been applied to all of them. If you execute the command find() to show the companies alone in the projection, this fact will be confirmed.
You can see that it no longer has the fields name.last and organization, because these were not specified in the document that was supplied to the command replaceOne(). It just replaces the document with the one supplied, except for the field ObjectId. Being the primary key, this field cannot be changed via an updateOne() or a replaceOne().
The delete operation takes a filter and removes the document from the collection. The filter format is the same, and the variations deleteOne() and deleteMany() are both available, just as in the update operation.
The find() method is used to return all the documents or a subset of the documents in a collection. Many a time, instead of the list of documents, we need a summary or an aggregate, for example, the count of documents that match a certain criterion.
The count() method can surely take a filter. But what about other aggregate functions, such as sum? That is where the aggregate() comes into play. When compared to relational databases supporting SQL, the aggregate() method performs the function of the GROUP BY clause. But it can also perform other functions such as a join, or even an unwind (expand the documents based on arrays within), and much more.
You can look up the advanced features that the aggregate() function supports in the MongoDB documentation at https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/ but for now, let’s look at the real aggregation and grouping construct that it provides.
The aggregate() method works in a pipeline. Every stage in the pipeline takes the input from the result of the previous stage and operates as per its specification to result in a new modified set of documents. The initial input to the pipeline is, of course, the entire collection. The pipeline specification is in the form of an array of objects, each element being an object with one property that identifies the pipeline stage type and the value specifying the pipeline’s effect.
For example, the find() method can be replicated using aggregate() by using the stages $match (the filter) and $project (the projection). To perform an actual aggregation, the $group stage needs to be used. The stage’s specification includes the grouping key identified by the property _id and other fields as keys, whose values are aggregation specifications and fields on which the aggregation needs to be performed. The _id can be null to group on the entire collection.
There are other aggregation functions, including minimum and maximum. For the complete set, refer to the documentation at https://docs.mongodb.com/manual/reference/operator/aggregation/group/#accumulator-operator .
Write a simple statement to retrieve all employees who have middle names. Hint: Look up the MongoDB documentation for query operators at https://docs.mongodb.com/manual/reference/operator/query/ .
Is the filter specification a JSON? Hint: Think about date objects and quotes around field names.
Say an employee’s middle name was set mistakenly, and you need to remove it. Write a statement to do this. Hint: Look up the MongoDB documentation for update operators at https://docs.mongodb.com/manual/reference/operator/update/ .
During index creation, what did the 1 indicate? What other valid values are allowed? Hint: Look up the MongoDB indexes documentation at https://docs.mongodb.com/manual/indexes/ .
Answers are available at the end of the chapter.
This is the Node.js driver that lets you connect and interact with the MongoDB server. It provides methods very similar to what you saw in the mongo shell, but not exactly the same. Instead of the low-level MongoDB driver, we could use an Object Document Mapper called Mongoose, which has a higher level of abstraction and more convenient methods. But learning about the lower-level MongoDB driver may give you a better handle on the actual working of MongoDB itself, so I’ve chosen to use the low-level driver for the Issue Tracker application.
Let’s also start a new Node.js program just to try out the different ways that the driver’s methods can be used. In the next section, we’ll use some code from this trial to integrate the driver into the Issue Tracker application. Let’s call this sample Node.js program trymongo.js and place it in a new directory called scripts, to distinguish it from other files that are part of the application.
The URL should start with mongodb:// followed by the hostname or the IP address of the server to connect to. An optional port can be added using : as the separator, but it’s not required if the MongoDB server is running on the default port, 27017. It’s good practice to separate the connection parameters into a configuration file rather than keep them in a checked-in file, but we’ll do this in the next chapter. For the moment, let’s hard code this. If you have used one of the cloud providers, the URL can be obtained from the corresponding connection instructions. For the local installation, the URL will be mongodb://localhost/issuetracker. Note that the MongoDB Node.js driver accepts the database name as part of the URL itself, and it is best to specify it this way, even though a cloud provider may not show this explicitly.
With this collection, we can do the same things we did with the mongo shell’s equivalent db.employees in the previous section. The methods are also very similar, except that they are all asynchronous. This means that the methods take in the regular arguments, but also a callback function that’s called when the operation completes. The convention in the callback functions is to pass the error as the first argument and the result of the operation as the second argument. You already saw this pattern of callback in the previous connection method.
Note that accessing the collection and the insert operation can only be called within the callback of the connection operation, because only then do we know that the connection has succeeded. There also needs to be some amount of error handling, but let’s deal with this a little later.
Let’s put all this together in a function called testWithCallbacks(). We will soon also use a different method of using the Node.js driver using async/await. Also, as is customary, let’s pass a callback function to this function, which we will call from the testWithCallbacks() function once all the operations are completed. Then, if there are any errors, these can be passed to the callback function.
Close the connection to the server
Call the callback
Return from the call, so that no more operations are performed
With all the error handling and callbacks introduced, the final code in the trymongo.js file is shown in Listing 6-1.
Although no effort has been spared to ensure that all code listings are accurate, there may be typos or even corrections that did not make it to the book before it went to press. So, always rely on the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ) as the tested and up-to-date source for all code listings, especially if something does not work as expected.
As you probably felt yourself, the callback paradigm is a bit unwieldy. But the advantage is that it works in the older JavaScript version (ES5), and therefore, older versions of Node.js. The callbacks are bit too deeply nested and the error handling makes for repetitive code. ES2015 started supporting Promises, which is supported by the Node.js MongoDB driver as well, and this was an improvement over callbacks. But in ES2017 and Node.js from version 7.6, full support for the async/await paradigm appeared, and this is the recommended and most convenient way to use the driver.
Errors will be thrown and can be caught. We can place all the operations in a single try block and catch any error in one place (the catch block) rather than after each call. There is no need for the function to take a callback, because if the caller needs to wait for the result, an await can be added before the call to this function, and errors can be thrown.
A good way to test whether errors are being caught and displayed is by running the program again. There will be errors because we have a unique index on the field id, so MongoDB will throw a duplicate key violation. If you have dropped the collection after creating the index, you could run the createIndex() command to reinstate this index.
As you can see, the async/await paradigm is much smaller in terms of code, as well as a lot clearer and easier to read. In fact, although we caught the error within this function, we didn’t have to do it. We could as well have let the caller handle it.
Given the benefits of the async/await paradigm, let’s use this in the Issue Tracker application when interacting with the database.
The mongo shell is not only an interactive shell, but is also a scripting environment. Using this, scripts can be written to perform various tasks such as schema initialization and migration. Because the mongo shell is in fact built on top of a JavaScript engine, the power of JavaScript is available in the scripts, just as in the shell itself.
One difference between the interactive and the non-interactive mode of working is that the non-interactive shell does not support non-JavaScript shortcuts, such as use <db> and show collections commands. The script has to be a regular JavaScript program adhering to the proper syntax.
Let’s create a schema initialization script called init.mongo.js within the script directory. Since MongoDB does not enforce a schema, there is really no such thing as a schema initialization as you may do in relational databases, like creation of tables. The only thing that is really useful is the creation of indexes, which are one-time tasks. While we’re at it, let’s also initialize the database with some sample documents to ease testing. We will use the same database called issuetracker that we used to try out the mongo shell, to store all the collections relevant to the Issue Tracker application.
Let’s copy the array of issues from server.js and use the same array to initialize the collection using insertMany() on a collection called issues. But before that, let’s clear existing issues it by calling a remove() with an empty filter (which will match all documents) on the same collection. Then, let’s create a few indexes on useful fields that we will be using to search the collection with.
For the other methods of using MongoDB, there are instructions as comments on the top of the script. In essence, the entire connection string has to be specified in the command line, including the username and password that you use to connect to the hosted service. Following the connection string, you can type the name of the script, scripts/init.mongo.js.
You can run this any time you wish to reset the database to its pristine state. You should see an output that indicates that two issues were inserted, among other things such as the MongoDB version and the shell version. Note that creating an index when one already exists has no effect, so it is safe to create the index multiple times.
The same schema initialization could have been done using a Node.js script and the MongoDB driver. What are the pros and cons of each of these methods: using the mongo shell vs. the Node.js MongoDB driver?
Are there any other indexes that may be useful? Hint: What if we needed a search bar in the application? Read about MongoDB index types at https://docs.mongodb.com/manual/indexes/#index-types .
Answers are available at the end of the chapter.
In the previous section, you saw how to use the Node.js driver to perform basic CRUD tasks. With this knowledge, let’s now change the List API to read from the MongoDB database rather than the in-memory array of issues in the server. Since we’ve initialized the database with the same initial set of issues, while testing, you should see the same set of issues in the UI.
We did not have to do anything special due to the fact that the resolver issueList() is now an async function, which does not immediately return a value. The graphql-tools library handles this automatically. A resolver can return a value immediately or return a Promise (which is what an async function returns immediately). Both are acceptable return values for a resolver.
Now, assuming that the server is still running (or that you have restarted the server and the compilation), if you refresh the browser, you will find that the two initial sets of issues are listed in a table, as before. The UI itself will show no change, but to convince yourself that the data is indeed coming from the database, you could modify the documents in the collection using the mongo shell and the updateMany() method on the collection. If, for example, you update effort to 100 for all the documents and refresh the browser, you should see that the effort is indeed showing 100 for all the rows in the table.
We are saving the connection in a global variable. What happens when the connection is lost? Stop the MongoDB server and start it again to see what happens. Does the connection still work?
Shut down the MongoDB server, wait for a minute or more, and then start the server again. Now, refresh the browser. What happens? Can you explain this? What if you wanted a longer period for the connection to work even if the database server is down? Hint: Look up the connection settings parameters at http://mongodb.github.io/node-mongodb-native/3.1/reference/connecting/connection-settings/ .
We used toArray() to convert the list of issues into an array. What if the list is too big, say, a million documents? How would you deal with this? Hint: Look up the documentation for the MongoDB Node.js driver's Cursor at http://mongodb.github.io/node-mongodb-native/3.1/api/Cursor.html . Note that the find() method returns a Cursor.
In order to completely replace the in-memory database on the server, we’ll also need to change the Create API to use the MongoDB database. As you saw in the MongoDB CRUD Operations section, the way to create a new document is to use the insertOne() method on the collection.
We used the size of the in-memory array to generate the new document’s id field. We could do the same, using the count() method of the collection to get the next ID. But there is a small chance when there are multiple users using the application that a new document is created between the time we call the count() method and the time we call the insertOne() method. What we really need is a reliable way of generating a sequence of numbers that cannot give us duplicates, much like sequences in popular relational databases.
MongoDB does not provide such a method directly. But it does support an atomic update operation, which can return the result of the update. This method is called findOneAndUpdate(). Using this method, we can update a counter and return the updated value, but instead of using the $set operator, we can use the $inc operator, which increments the current value.
Let’s first create a collection with the counter that holds a value for the latest Issue ID generated. To make it a bit generic, let’s assume we may have other such counters and use a collection with an ID set to the name of the counter and a value field called current holding the current value of the counter. In the future, we could add more counters in the same collections, and these would translate to one document for each counter.
Now, a call to findOneAndUpdate() that increments the current field is guaranteed to return a unique value that is next in the sequence. Let’s create a function in server.js that does this, but in a generic manner. We’ll let it take the ID of the counter and return the next sequence. In this function, all we have to do is call findOneAndUpdate(). It identifies the counter to use using the ID supplied, increments the field called current, and returns the new value. By default, the result of the findOneAndUpdate() method returns the original document. To make it return the new, modified document instead, the option returnOriginal has to be set to false.
The option for returning the current or new value is called differently in the Node.js driver and in the mongo shell. In the mongo shell, the option is called returnNewDocument and the default is false. In the Node.js driver, the option is called returnOriginal and the default is true. In both cases, the default behavior is to return the original, so the option must be specified to return the new document.
Testing this set of changes will show that new issues can be added, and even on a restart of the Node.js server, or the database server, the newly added issues are still there. As a cross-check, you could use the mongo shell to look at the contents of the collection after every change from the UI.
Could we have just added the _id to the passed-in object and returned that instead of doing a find() for the inserted object?
Answers are available at the end of the chapter.
In this chapter, you learned about the installation and other ways of getting access to an instance of a database in MongoDB. You saw how to use the mongo shell and the Node.js driver to access the basic operations in MongoDB: the CRUD operations. We then modified the Issue Tracker application to use some of these methods to read and write to the MongoDB database, thus making the issue list persistent.
I covered only the very basics of MongoDB, only the capabilities and features that will be useful to build the Issue Tracker application, which is a rather simple CRUD application. In reality, the capabilities of the database as well as the Node.js driver and the mongo shell are vast, and many more features of MongoDB may be required for a complex application. I encourage you to take a look at the MongoDB documentation ( https://docs.mongodb.com/manual/ ) and the Node.js driver documentation ( http://mongodb.github.io/node-mongodb-native/ ) to familiarize yourself with what else the database and the Node.js drivers are capable of.
Now that we have used the essentials of the MERN stack and have a working application, let’s take a break from implementing features and get a bit organized instead. Before the application gets any bigger and becomes unwieldy, let’s modularize the code and use tools to improve our productivity.
We’ll do this in the next chapter, by using Webpack, one of the best tools that can be used to modularize both the front-end and the back-end code.
As per the mongo shell documentation under "Access the mongo shell Help", you can find that there is a method called help() on many objects, including the cursor object. The way to get help on this is using db.collection.find().help().
But since this is also a JavaScript shell like Node.js, pressing Tab will auto-complete and a double-Tab will show a list of possible completions. Thus, if you assign a cursor to a variable and press Tab twice after typing the variable name and a dot after that, the shell will list the possible completions, and that is a list of methods available on the cursor.
This can be done using the $exists operator like this:
> db.employees.find({ "name.middle": { $exists: true } })
The filter specification is not a JSON document, because it is not a string. It is a regular JavaScript object, which is why you are able to skip the quotes around the property names. You will also be able to have real Date objects as field values, unlike a JSON string.
Although we supplied null as the value for $unset, this value is ignored. It can be anything.
The 1 indicates an ascending sort order for traversing the index. -1 is used to indicate a descending sort order. This is useful only for compound (aka composite) indexes, because a simple index on one field can be used to traverse the collection in both directions.
The advantage of using the Node.js driver is that there is one way of doing things across the application and the scripts, and the familiarity will help prevent errors. But running the program requires a proper Node.js environment, including npm modules installed, whereas the mongo shell script can be run from anywhere, provided the machine has the mongo shell installed.
A search bar is quite helpful when searching for issues. A text index (an index based on the words) on the title field would be useful in this case. We’ll implement a text index toward the end of the book.
The connection object is in fact a connection pool. It automatically determines the best thing to do: reuse an existing TCP connection, reestablish a new connection when the connection is broken, etc. Using a global variable (at least, reusing the connection object) is the recommended usage.
If the database is unavailable for a short period (less than 30 seconds), the driver retries and reconnects when the database is available again. If the database is unavailable for a longer period, the read throws an error. The driver is also unable to reestablish a connection when the database is restored. The application server needs to be restarted in this case.
The default interval of 30 seconds can be changed using the connection settings reconnectTries or reconnectInterval.
One option is to use limit() on the result to limit the return value to a maximum number of records. For example, find().limit(100) returns the first 100 documents. If you were to paginate the output in the UI, you could also use the skip() method to specify where to start the list.
If, on the other hand, you think the client can handle large lists but you don’t want to expend that much memory in the server, you could deal with one document at a time using hasNext() and next() and stream the results back to the client.
Adding the _id and returning the object passed in would have worked, so long as you know for a fact that the write was a success and the object was written to the database as is. In most cases, this would be true, but it’s good practice to get the results from the database, as that is the ultimate truth.
In this chapter and the next, we’ll take a break from adding features. Instead, we’ll get a bit more organized in preparation for when the application grows larger and larger.
In this chapter, we’ll look again at the architecture and make it more flexible so that it can cater to larger applications with lots of traffic. We’ll use a package called dotenv to help us run the same code on different environments using different configurations for each environment, such as development and production.
Finally, we’ll add checks to verify that the code we write follows standards and good practices and catches possible bugs earlier in the testing cycle. We’ll use ESLint for this purpose.

Single server architecture
All requests land on the same physical server, and within that is the one and only Express application. Then, the requests are routed into two different middleware depending on the request. Any request-matching files in the public directory are matched by the middleware called static. This middleware uses the disk to read files and serve the file’s contents. Other requests that match the /graphql path are dealt with by the Apollo Server’s middleware. This middleware, using resolvers, gets the data from the MongoDB database.
The API has other consumers, not just the browser-based UI. For example, the API may be exposed to third parties or mobile applications.
The two parts have different scaling requirements. Typically, as more consumers of the API appear, you may need multiple servers for the API and a load-balancer. Whereas, since most static content can and will be cached in the browsers, having many servers for static assets may be overkill.
Further, the fact that both functions are being done on the same server, both inside the same Node.js and Express process, makes it harder to diagnose and debug performance issues. A better option is to separate the two functions into two servers: one that serves static content, and another that hosts just the API.
In later chapters, I introduce server rendering, wherein complete pages will be generated from the server as opposed to being constructed on the browser. This helps search engines index the pages correctly, because search engine bots do not necessarily run JavaScript. When we implement server rendering, it will help if all the API code and the UI code are kept separate.

Separate UI server architectures
In the diagram in Figure 7-2, you can see that there are two servers: the UI server and the API server. These can be physically different computers, but for development purposes, we’ll run these on the same computer but on different ports. These will be run using two different Node.js processes, each with its own instance of Express.
The API server will now be responsible for handling only the API requests, and therefore, it will respond only to URLs matching /graphql in the path. Thus, the Apollo server middleware and its requests to the MongoDB database will be the only middleware in the API server.
The UI server part will now contain only the static middleware. In the future, when we introduce server rendering, this server will be responsible for generating HTML pages by calling the API server’s APIs to fetch the necessary data. For the moment, we’ll be using the UI server only for serving all static content, which consists of index.html and the JavaScript bundle that contains all the React code.
The browser will be responsible for using the appropriate server based on the type of request: all API calls will be directed to the API server, whereas the static files will be referred to the UI server.
The first thing we’ll do to achieve this is create a new directory structure that cleanly separates the UI and the API code.
Ideally, the UI and API code would belong in two different repositories, because there is nothing that is shared among them. But for the convenience of reading this book and referring to Git diffs from the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ), I have kept the code together but in different directories at the top-most level.
The commands that are shown (which can also be found in the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 in the file commands.md) are meant for execution in MacOS or the bash shell in a Linux-based distribution. If you are using a Windows PC, you will have to use the Windows equivalent commands.

New directory structure for UI server separation
Let’s now create two new package.json files in the two new directories. You could also copy this file from the root project directory for convenience and make changes.
Although no effort has been spared to ensure that all code listings are accurate, there may be typos or even corrections that did not make it into the book before it went to press. So, always rely on the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ) as the tested and up-to-date source for all code listings, especially if something does not work as expected.
At this point in time, you should be able to run the API server using npm start. Further, if you test the APIs using the GraphQL Playground, you should find that the APIs are working as before.
Open a new browser tab and type http://localhost:3000. What do you see, and why? Do we need to do anything about this? What are the options? Hint: Browse to GitHub’s API endpoint host in a similar manner, at https://api.github.com .
Answers are available at the end of the chapter.
We postponed removing hard-coded things such as the port numbers and MongoDB URL. Now that the directory structure has been finalized, it’s perhaps a good time to remove all hard-coding and keep these as variables that can be changed more easily.
Typically, there would be three deployment environments: development, staging, and production. The server ports and MongoDB URL for each of these would be quite different. For example, the ports of both the API server and the UI server would be 80. We used two different ports because both servers were run on the same host, and two processes cannot listen on the same port. Also, we used ports like 8000 because using port 80 requires administrative privileges (superuser rights).
Rather than predetermine the ports and the MongoDB URL based on possible deployment targets such as development, staging, and production, let’s keep the variables flexible so that they can be set to anything at runtime. A typical way of supplying these is via environment variables, especially for remote targets and production servers. But during development, it’s nice to be able to have these in some configuration file so that the developers don’t need to remember to set these up every time.
Let’s use a package called dotenv to help us achieve this. This package can convert variables stored in a file into environment variables. Thus, in the code, we only deal with environment variables, but the environment variables can be supplied via real environment variables or configuration files.
In the code, all we’d have to do is look for the environment variable DB_URL using process.env.DB_URL and use the value from it. This value can be overridden by an actual environment variable defined before starting the program, so it is not necessary to have this file. In fact, most production deployments would take the value only from the environment variable.
It is recommended that the .env file itself not be checked into any repository. Each developer and deployment environments must specifically set the variables in the environment or in this file as per their needs. This is so that changes to this file remain in the developer’s computer and others’ changes don’t overwrite a developer’s settings.
Now, if you specify API_SERVER_PORT as 4000 in the file .env and restart the API server (because nodemon needs to know the new watch files), you should see that it now uses port 4000. You could undo this change and instead define an environment variable (do not forget to use export in the bash shell to make the variable available to sub-processes) and see that the change has indeed been made. Note that the actual environment variables take precedence over (or override) the same variable defined in the .env file.
You can now test the script using the command line and Node.js as before, and you’ll see the effect of different environment variables, both in the shell as well as in the .env file.
The UI server port
The API endpoint to call
Unlike these changes, the API endpoint has to somehow get to the browser in the form of JavaScript code. It is not something that can be read from an environment variable, because that is not transmitted to the browser.
One way to do this is to replace a predefined string within the code with the variable’s value as part of the build and bundle process. I’ll describe this method in the next section. Although this is a valid and preferred choice for many people, I have chosen to make the configuration a runtime variable as opposed to a compile-time one. That’s because on the real UI servers, the way to set the server port and the API endpoint will be uniform.
Now, if you test the application with the default ports and endpoints, the application should be working as before. If you have been running npm run watch in a console, the changes to App.jsx would have been automatically recompiled.
You can also ensure that a change to any of the variables, both via an actual environment variable and a change in the .env file (if you have one), do take effect. If you change a variable via an environment variable do remember to export it if you are using the bash shell. Also, the server has to be restarted since nodemon does not watch for changes to any environment variable.
In the browser, manually type http://localhost:8000/env.js. What do you see? Set the environment variable UI_API_ENDPOINT to a different location and restart the UI server. Check the contents of env.js.
Answers are available at the end of the chapter.
If you had your Network tab open in the Developer Console while testing, you would have noticed that there are two calls to /graphql instead of just one. The HTTP method for the first call is OPTIONS. The reason is that the API call is to a host (http://localhost:3000) that is different from the origin of the application (http://localhost:8000). Due to the Same-origin policy, such requests are normally blocked by the browser unless the server specifically allows it.
The Same-origin policy exists to prevent malicious websites from gaining unauthorized access to the application. You can read the details of this policy at https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy . But the gist of it is that since cookies set by one origin are automatically attached with any request to that origin, it is possible that a malicious website can make a call to the origin from the browser and the browser will attach the cookie.
Say you are logged into a bank’s website. In another browser tab, you are browsing some news website that has some malicious JavaScript running in it, maybe via an advertisement on the website. If this malicious JavaScript makes an Ajax call to the bank’s website and the cookies are sent as part of the request, the malicious JavaScript can end up impersonating you and maybe even transferring funds to the hacker’s account!
If you test the application, you will find that the OPTION request fails with an HTTP response of 405. Now, the application is safe from malicious cross-site attacks. But this also means that we need some other mechanism to make API calls.

Proxy-based architecture
Now, a proxy can be used as a middleware that the package provides, mounted on the path /graphql, using app.use(). The middleware can be created with just a single option: the target of the proxy, which is the base URL of the host where the requests have to be proxied. Let’s define another environment variable called API_PROXY_TARGET and use its value as the target. If this variable is undefined, we can skip installing the proxy rather than default it.
Now, if you test the application and look at the Network tab in the browser’s Developer Console, you will find that there is only one request going to the UI server (port 8000) for each of the API calls, which successfully execute.
You can use the proxy method as described in this section or let the UI make direct calls to the API server and enable CORS in the API server. Both options are equally good and your actual choice depends on various things, such as your deployment environment and the security needs of your application.
For the purpose of following this book, let’s revert the changes to the .env files we did as part of this section so that we use the direct API call mechanism. You may copy the sample.env file in both the API and the UI directories from the GitHub repository to your own .env files, which reflects the direct API way of working.
A linter (something that lints) checks for suspicious code that could be bugs. It can also check whether your code adheres to conventions and standards that you want to follow across your team to make the code predictably readable.
While there are multiple opinions and debates on what is a good standard (tabs vs. spaces, for example), there has been no debate on whether there needs to be a standard in the first place. For one team or one project, adopting one standard is far more important than adopting the right standard.
ESLint ( https://eslint.org ) is a very flexible linter that lets you define the rules that you want to follow. But we need something to start off with and the rule set that has appealed to me the most has been that of Airbnb. Part of the reason for its appeal has been its popularity: if more people adopt it, the more standardized it gets, so more people end up following it, becoming a virtuous cycle.
ESLint looks for a set of rules in the .eslintrc file, which is a JSON specification. These are not the definition of rules, instead, a specification of which rules need to be enabled or disabled. Rule sets also can be inherited, which is what we’ll do using the extends property in the configuration. Using a .eslintrc file makes the rules apply to all the files in that directory. For overrides in a single file, rules can be specified within comments in that file, or even just a single line.
Alternatively, you can install a plugin in your editor that shows lint errors in the editor itself. The popular code editors Atom and Sublime do have plugins to handle this; follow the instructions on their respective websites to install the plugins. Then, we’ll look at every type of error or warning and deal with it.
For most of the errors, we are just going to change the code to adhere to the suggested standard. But in a few cases, we will make exceptions to the Airbnb rule. This could be for the entire project or, in some cases, for a specific file or line in a file.
Let’s look at each type of error and fix them. Note that I am only discussing the errors that ESLint would have found in the code we’ve written up to now. As we write more code, we’ll fix any lint errors reported then and there, so an editor plugin to report errors while we type is highly recommended.
Indentation: Consistent indentation is expected throughout; this needs no justification. Let’s fix any violations.
Keyword spacing: Spaces between a keyword (if, catch, etc.) and the opening parenthesis is recommended. Let’s change the code wherever this is reported.
Missing semicolon: There’s a lot of debate on whether semicolons everywhere or semicolons nowhere is better. Both work, except for a few cases where the absence of semicolons causes a behavior change. If you follow the no-semicolons standard, you must remember those special cases. Let’s go with the Airbnb default, which is to require a semicolon everywhere.
Strings must use single quotes: JavaScript allows both single and double quotes. In order to standardize, it’s better to consistently use one and only one style. Let’s use the Airbnb default, single quotes.
Object properties on a new line: Either all properties of an object have to be in one line, or each property in a new line. It just makes it more predictable, especially when a new property has to be inserted. There is no doubt as to whether to append the new property to one of the existing lines or in a new line.
Space after { and before } in objects: This is just for readability; let’s change it wherever the linter reports an error.
Arrow function style: The linter recommends using either parentheses around a single argument and a function body in curly braces, or no parentheses around the argument and a return expression (i.e., not a function body). Let’s make the suggested corrections.
Functions must be named: Omitting function names makes it harder to debug because the stack trace cannot identify the function. But this applies only to regular functions, not the arrow-style functions, which are expected to be short pieces of callbacks.
Consistent return: Functions should always return a value or never return a value, regardless of conditions. This reminds developers to add a return value or be explicit about it, just in case they have forgotten to return a value outside a condition.
Variables must be defined before use: Although JavaScript hoists definitions such that they are available throughout the file, it’s good practice to define them before use. Otherwise, it can get confusing while reading the code top to bottom.
Console: Especially in the browser, these are typically leftover debug messages, and therefore are not suitable to be shown in the client. But these are fine in a Node.js application. So, let’s switch this rule off in the API code.
Returning in assignments : Though it is concise, a return and an assignment together can be confusing to the reader. Let’s avoid it.
Redeclaring variables: It’s hard to read and understand the intent of the original coder when a variable shadows (overwrites) another variable in a higher scope. It’s also impossible to access the variable in the higher scope, so it’s best to give different names to variables.
Prefer arrow callback: When using anonymous functions (as when passing a callback to another function), it’s better to use the arrow function style. This has the added effect of setting the variable this to the current context, which is desirable in most cases, and the syntax is also more concise. If the function is large, it’s better to separate it out to a named regular function.
Triple-equals: Usage of triple-equals ensures that the values are not coerced before comparison. In most cases, this is what is intended, and it avoids errors due to coerced values.
Assignment to function parameters: Mutating passed-in parameters may cause the caller to be unware of the change and therefore unexpected behavior. Let’s avoid changing function parameters’ values and instead make a copy of the parameter.
Wrap immediately invoked function expressions (IIFE) : An immediately invoked function expression is a single unit. Wrapping it in parentheses not only makes it clearer, but also makes it an expression rather than a declaration.
Another way of ignoring patterns of files is to add them as lines to a text file called .eslintignore. This is useful when there are many patterns to be ignored. Since we need only one directory to be ignored, we’ll use the command line option.
Object properties on a new line
Missing semicolon
Strings must use single quotes
Consistent return
Spacing around curly braces in object definitions
Space after { and before } in objects
Triple-equals
Let’s discuss the rest of the issues shown by ESLint.
Implicit arrow line break: This is a stylistic issue, to maintain consistency of line breaks. It is recommended to keep the expression returned from an arrow function on the same line as the arrow. If the expression is long and cannot fit on one line, it can be enclosed in parentheses starting on the same line. Let’s make this change.
Infix operators must be spaced: For readability, operators need spaces around them. Let’s make the change as suggested.
'React' must be in scope: When ESLint detects JSX, it expects that React be defined. At this stage, we are including React from the CDN. Soon, we will be using this and other modules by installing them using npm. Until then, let’s disable these checks. We’ll do these inline, keeping the .eslintrc contents free of such temporary workarounds. Let’s add the following comments for this in the App.jsx file:
Stateless functions: The component IssueFilter currently is just a placeholder. When we do add functionality to it, it will become a stateful component. Until then, let’s disable the ESLint check, but only for this component.
Prefer destructuring, especially props assignment: Not only is the new style of assigning variables from objects more concise and readable, it also saves temporary references for those properties being created. Let’s change the code as recommended.
One component per file: Declaring only one component per file improves readability and reusability of components. At the moment, we have not discussed how to create multiple files for the React code. We will do that in the next chapter; until then, let’s disable the check for the file.
No alerts: The original intention of this rule was to weed out debugging messages that were left unpruned. We’ll be converting alert messages to nicely styled messages within the document. Until then, let’s disable this check, but only in files where we are showing error messages.
Missing trailing comma: Requiring a comma for the last item in a multi-line array or object is really handy when inserting new items. In addition, when looking at the difference between two versions in say, GitHub, the fact that a comma is added to the last line suggests that the line has changed, whereas in reality, it hasn’t.
Props validation: It’s good practice to check for the types of properties being passed in to a component, both to make it explicit to the user of the component, and to avoid errors in input. Although I’ll discuss this briefly, I’ll not be adding the props validation in React code, purely to avoid distraction in the code listings. Let’s switch off this rule globally for the Issue Tracker application, but I encourage you to keep this enabled in your own application.
Button type: Although the default type of a button is submit, it is better to ensure that it is explicitly stated, just in case this is not the behavior that is intended, and the developer has missed adding a type. Let’s follow the recommendation and add submit to the type of the button.
Function parameter reassignment: Assigning to a function parameter can make the original parameter inaccessible and lead to confusing behavior. Let’s use a new variable rather than reuse the function parameter.
Now, the command npm run lint will check the current set, and any other files that will be added under the UI directory. After these code changes, the command should return no errors or warnings.
In strongly typed languages such as Java, the type of parameters is always predetermined and specified as part of the function declaration. This ensures that the caller knows the list and the types of parameters and ensures that passed-in parameters are validated against this specification.
The IssueTable and IssueRow components need an object and an array of objects as properties, respectively. Although PropTypes supports data types such as arrays and objects, ESLint considers these to be too vague. Instead, the actual shape of the object has to be described, which means each field of the object and its data type has to be specified to avoid the ESLint warning.
Though it is a great idea to add PropTypes-based validation for all components, for the purpose of this book, I’ll be skipping this. The only reason is that it makes the code more verbose and could distract readers from the main focus of changes.
Although we did not add any features to the application in this chapter, we made a big architectural change by separating the UI and the API servers. We discussed implications of CORS and coded an option for dealing with it using a proxy.
Then, you saw how to make the application configurable for different deployment environments like staging and production. We also sanitized the code by adding checks for adhering to coding standards, best practices, and validations.
In the next chapter, we will continue to improve developer productivity by modularizing the code (i.e., splitting single large files into smaller, reusable pieces) and adding support for debugging and other tools useful during the development process.
You should see a message in the browser like Cannot GET /. This is a message returned by the Express server since there is no route present for /. By itself, it’s not a problem since the only consumer of the API is the web UI, and within our control. If, on the other hand, the API were exposed to others like GitHub’s API, it would indeed be better to return a helpful message indicating where the real API endpoints are.
Another option is to host the API on the root (/) rather than on /graphql. But having /graphql as the endpoint name makes it explicit that it’s a GraphQL API.
The contents of env.js will show a JavaScript assignment of window.ENV to an object with the UI_API_ENDPOINT property. Restarting the UI server after changes to the environment will cause the contents to reflect the new value.
We started to get organized in the previous chapter by changing the architecture and adding checks for coding standards and best practices. In this chapter, we’ll take this a little further by splitting the code into multiple files and adding tools to ease the process of development. We’ll use Webpack to help us split front-end code into component-based files, inject code into the browser incrementally, and refresh the browser automatically on front-end code changes.
Download the code from the book’s GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ) as of the end of this chapter and use that as your starting point for your project.
Use the starter-kit create-react-app ( https://github.com/facebook/create-react-app ) to start your new React app and add code for your application. But note that create-react-app deals only with the React part of the MERN stack; you will have to deal with the APIs and MongoDB yourself.
Use mern.io ( http://mern.io ) to create the entire application’s directory structure, which includes the entire MERN stack.
But if you are an architect or just setting up the project for your team, it is important to understand how tools help developer productivity and how you can have finer control over the whole build and deployment process as well. In which case, I encourage you not to skip this chapter, even if you use one of these scaffolding tools, so that you learn about what exactly happens under the hood.
You saw all this while in api/server.js how to include modules in a Node.js file. After installing the module, we used the built-in function require() to include it. There are various standards for modularization in JavaScript, of which Node.js has implemented a slight variation of the CommonJS standard. In this system, there are essentially two key elements to interact with the module system: require and exports.
The require() element is a function that can be used to import symbols from another module. The parameter passed to require() is the ID of the module. In Node's implementation, the ID is the name of the module. For packages installed using npm, this is the same as the name of the package and the same as the sub-directory inside the node_modules directory where the package’s files are installed. For modules within the same application, the ID is the path of the file that needs to be imported.
Now, whatever is exported by other.js will be available in the other variable. This is controlled by the other element we talked about: exports. The main symbol that a file or module exports must be set in a global variable called module.exports within that file, and that is the one that will be returned by the function call to require(). If there are multiple symbols, they can be all set as properties in an object, and we can access those by dereferencing the object or by using the destructuring assignment.
require() statements to import GraphQLScalarType and Kind from the other packages.
The variable module.exports to be set to the function, so that it can be used after importing the file.
As you can see, whatever was assigned to module.exports is the value that a call to require() returns. Now, this variable, GraphQLDate can be seamlessly used in the resolver, as before. But we won’t be making this change in server.js just yet, because we’ll be making more changes to this file.
This change could be in server.js , but we’ll be separating all this into one file that deals with Apollo Server, the schema, and the resolvers. Let’s create that file now and call it api/api_handler.js. Let’s move the construction of the resolvers object and the creation of the Apollo server into this file. As for the actual resolver implementations, we’ll import them from three other files—graphql_date.js, about.js, and issue.js.
As for the export from this file, let’s export a function that will do what applyMiddleware() did as part of server.js. We can call this function installHandler() and just call applyMiddleware() within this function.
We have not created issue.js yet, which is needed for importing the issue-related resolvers. But before that, let’s separate the database connection creation and a function to get the connection handler into a new file. The issue.js file will need this DB connection, etc.
Let’s call the file with all database-related code db.js and place it in the API directory. Let’s move the functions connectToDb() and getNextSequence() into this file, as also the global variable db, which stores the result of the connection. Let’s export the two functions as they are. As for the global connection variable, let’s expose it via a getter function called getDb(). The global variable url can also now be moved into the function connectDb() itself.
Now, the application is ready to be tested. You can do that via the Playground as well as using the Issue Tracker application UI to ensure that things are working as they were before the modularization of the API server code.
In this section, we’ll deal with the front-end, or the UI code, which is all in one big file, called App.jsx. Traditionally, the approach to using split client-side JavaScript code was to use multiple files and include them all (or whichever are required) using <script> tags in the main HTML file or index.html. This is less than ideal because the dependency management is done by the developer, by maintaining a certain order of files in the HTML file. Further, when the number of files becomes large, this becomes unmanageable.
Tools such as Webpack and Browserify provide alternatives. Using these tools, dependencies can be defined using statements equivalent to require() that we used in Node.js. The tools then automatically determines not just the application’s own dependent modules, but also third-party library dependencies. Then, they put together these individual files into one or just a few bundles of pure JavaScript that has all the required code that can be included in the HTML file.
The only downside is that this requires a build step. But then, the application already has a build step to transform JSX and ES2015 into plain JavaScript. It’s not much of a change to let the build step also create a bundle based on multiple files. Both Webpack and Browserify are good tools and can be used to achieve the goals. But I chose Webpack, because it is simpler to get all that we want done, which includes separate bundles for third-party libraries and our own modules. It has a single pipeline to transform, bundle, and watch for changes and generate new bundles as fast as possible.
If you choose Browserify instead, you will need other task runners such as gulp or grunt to automate watching and adding multiple transforms. This is because Browserify does only one thing: bundle. In order to combine bundle and transform (using Babel) and watch for file changes, you need something that puts all of them together, and gulp is one such utility. In comparison, Webpack (with a little help from loaders, which we’ll explore soon) can not only bundle, but can also do many more things such as transforming and watching for changes to files. You don’t need additional task runners to use Webpack.
Note that Webpack can also handle other static assets such as CSS files. It can even split the bundles such that they can be loaded asynchronously. We will not be exercising those aspects of Webpack; instead, we’ll focus on the goal of being able to modularize the client-side code, which is mainly JavaScript at this point in time.
The differences between the two modes are the various things Webpack does automatically, such as removing the names of modules, minifying, etc. It’s good to have all these optimizations when building a bundle for deploying in production, but these may hamper debugging and efficient development process.
The resulting file app.bundle.js is hardly interesting and is not very different from App.js itself. Note also that we didn’t run it against the React file App.jsx , because Webpack cannot handle JSX natively. All it did in this case was minify App.js. We did it just to make sure we’ve installed Webpack correctly and are able to run it and create output. To actually let Webpack figure out dependencies and put together multiple files, let’s split the single file App.jsx into two, by taking out the function graphQLFetch and placing it in a separate file.
We could, just as in the back-end code, use the require way of importing other files. But you will notice that most front-end code examples on the Internet use the ES2015-style modules using the import keyword. This is a newer and more readable way of importing. Even Node.js has support for the import statement, but as of version 10 of Node.js, it is experimental. If not, it could have been used for the back-end code as well. Using import makes it mandatory to use Webpack. So, let’s use the ES2015-style import for the front-end code only.
If you test the application at this point, you should find it working just as before. For good measure, you could also check in the Network tab of the Developer Console in the browser that it is indeed app.bundle.js that is being fetched from the server.
So, now that you know how to use multiple files in the front-end code, we could possibly create many more files like graphQLFetch.js for convenience and modularization. But the process was not simple, as we had to manually Babel transform the files first, and then put them together in a bundle using Webpack. And any manual step can be error prone: one can easily forget the transform and end up bundling an older version of the transformed file.
The good news is that Webpack is capable of combining these two steps, obviating the need for intermediate files. But it can’t do that on its own; it needs some helpers called loaders. All transforms and file types other than pure JavaScript require loaders in Webpack. These are separate packages. To be able to run Babel transforms, we’ll need the Babel loader.
Note that we did not have to supply any further options to the Babel loader, since all Webpack did was use the existing Babel transformer. And this used the existing configuration from .babelrc in the src directory.
Note the last line in the output: +1 hidden module. What this really means is that App.jsx was not transformed when only graphQLFetch.js was changed. This is a good time to change the npm scripts for compile and watch to use the Webpack command instead of the Babel command.
Webpack has two modes, production and development, which change the kind of optimizations that are added during transformation. Let’s assume that during development we’ll always use the watch script and to build a bundle for deployment, we’ll use the mode as production. The command line parameters override what is specified in the configuration file, so we can set the mode accordingly in the npm scripts.
Now, we are ready to split the App.jsx file into many files. It is recommended that each React component be placed in its own file, especially if the component is a stateful one. Stateless components can be combined with other components when convenient and when they belong together.
So, let’s separate the component IssueList from App.jsx. Then, let’s also separate the first level of components in the hierarchy—IssueFilter, IssueTable, and IssueAdd—into their own files. In each of these, we will export the main component. App.jsx will import IssueList.jsx, which in turn will import the other three components. IssueList.jsx will also need to import graphQLFetch.js since it makes the Ajax calls.
Let’s also move or copy the ESLint exceptions to the appropriate new files. All files will have an exception for declaring React as global; IssueFilter will also have the exception for stateless components.
If you run npm run watch from the ui directory , you will find that all the files are being transformed and bundled into app.bundle.js. If you now test the application, it should work just as before.
Save any JSX file with only a spacing change, while running npm run watch. Does Webpack rebuild the bundle? Why not?
Was it necessary to separate the mounting of the component (in App.jsx) and the component itself (IssueList) into different files? Hint: Think about what other pages we’ll need in the future.
What would happen if the default keyword was not used while exporting a class, say IssueList? Hint: Look up Mozilla Developer Network (MDN) documentation on the JavaScript export statement at https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export#Using_the_default_export .
Answers are available at the end of the chapter.
Until now, to keep things simple, we included third-party libraries as JavaScript directly from a CDN. Although this works great most times, we have to rely on the CDN services to be up and running to support our application. Further, there are many libraries that need to be included, and these too have dependencies among them.
In this section, we’ll use Webpack to create a bundle that includes these libraries. If you remember, I discussed that npm is used not only for server-side libraries, but also client-side ones. What’s more, Webpack understands this and can deal with client-side libraries installed via npm.
The fact that the bundle includes all of the libraries is a minor problem. The libraries will not change often, but the application code will, especially during the development and testing. Even when the application code undergoes a small change, the entire bundle is rebuilt, and therefore, a client will have to fetch the (now big) bundle from the server. We’re not taking advantage of the fact that the browser can cache scripts when they are not changed. This not only affects the development process, but even in production, users will not have the optimum experience.
If you now test the application (after starting the API and the UI servers, of course), you will find that the application works just as before. Further, a quick look at the Network tab in the Developer Console will show that the libraries are no longer being fetched from the CDN; instead, the new script vendor.bundle.js is being fetched from the UI server.
If you make a small change to any of the JSX files and refresh the browser, you will find that fetching vendor.bundle.js returns a “304 Not Modified” response, but the application’s bundle, app.bundle.js, is indeed being fetched. Given the sizes of the vendor.bundle.js file, it is a great saving of both time and bandwidth.
The watch mode of Webpack works well for client-side code, but there is a potential pitfall with this approach. You must keep an eye on the console that is running the command npm run watch to ensure that bundling is complete, before you refresh your browser to see the effect of your changes. If you press refresh a little too soon, you will end up with the previous version of your client-side code, scratch your head wondering why your changes didn’t work, and then spend time debugging.
Further, at the moment, we need an extra console for running npm run watch in the UI directory to detect changes and recompile the files. To solve these issues, Webpack has a powerful feature called Hot Module Replacement (HMR). This changes modules in the browser while the application is running, removing the need for a refresh altogether. Further, if there is any application state, that will also be retained, for example, if you were in the middle of typing in some text box, that will be retained because there is no page refresh. Most importantly, it saves time by updating only what is changed, and it removes the need for switching windows and pressing the refresh button.
There are two ways to implement HMR using Webpack. The first involves a new server called webpack-dev-server, which can be installed and run from the command line. It reads the contents of webpack.config.js and starts a server that serves the compiled files. This is the preferred method for applications without a dedicated UI server. But since we already have a UI server, it’s better to modify this slightly to do what the webpack-dev-server would do: compile, watch for changes, and implement HMR.
They need additional entry points (other than App.jsx) so that Webpack can build the client-side code necessary for this extra functionality into the bundle.
A plugin needs to be installed that generates incremental updates rather than entire bundles.
Note that the dev and hot middleware have to be installed before the static middleware. Otherwise, in case the bundles exist in the public directory (because npm run compile was executed some time back), the static module will find them and send them as a response, even before the dev and hot middleware get a chance.
npm run compile + npm run start: In production mode (the NODE_ENV variable is defined to production), starting of the server requires that npm run compile has been run and app.bundle.js and vendor.bundle.js have been generated and are available in the public directory.
npm run start: In development mode (NODE_ENV is either not defined or set to development), this starts the server with HMR enabled by default. Any changes to the source files will be hot replaced in the browser instantly.
npm run watch + npm run start, ENABLE_HMR=false: In development or production mode, these need to be run in two consoles. The watch command looks for changes and regenerates the JavaScript bundles, and the start command runs the server. Without ENABLE_HMR, the bundles will be served from the public directory, as generated by the watch command.
Thus, the IssueList component is constructed again and rendered, and almost everything gets refreshed. This can potentially lose local state. For example, if you had typed something in the text boxes for Owner and Title in the IssueAdd component, the text will be lost when you change IssueFilter.jsx.
To avoid this, we should ideally look for changes in every module and mount the component again, but preserve the local state. React does not have methods that make this possible, and even if it did, it would be tedious to do this in every component. To solve these problems, the react-hot-loader package was created. At compile time, it replaces a component’s methods with proxies, which then call the real methods such as render() . Then, when a component’s code is changed, it automatically refers to the new methods without having to remount the component.
This could prove to be useful in applications where local state is indeed important to preserve across refreshes. But for the Issue Tracker application, let’s not implement react-hot-loader, instead, let’s settle for the entire component hierarchy reloading when some code changes. It does not take that much time, in any case, and saves the complexity of installing and using react-hot-loader.
How can you tell that the browser is not being fully refreshed when a module’s code is changed? Use the Network section of your browser’s developer tools and watch what goes on.
Answers are available at the end of the chapter.
The unpleasant thing about compiling files is that the original source code gets lost, and if you have to put breakpoints in the debugger, it’s close to impossible, because the new code is hardly like the original. Creating a bundle of all the source files makes it worse, because you don’t even know where to start.
Fortunately, Webpack solves this problem by its ability to give you source maps, things that contain your original source code as you typed it in. The source maps also connect the line numbers in the transformed code to your original code. Browsers’ development tools understand source maps and correlate the two, so that breakpoints in the original source code are converted breakpoints in the transformed code.

Breakpoints in the original source code using source maps
You will find the sources in other browsers in a roughly similar manner, but not exactly the same. You may have to look around a little to locate them. For example in Safari, the sources can be seen under Sources -> app.bundle.js -> “” -> src.

React Developer Tools in the Chrome browser
The React Developer Tools has a compatibility problem with React version 16.6.0 at the time of writing this book. If you do face an issue (there will be an error in the console like Uncaught TypeError: Cannot read property 'displayName' of null), you may have to downgrade the version of React to 16.5.2.
You may not be comfortable with the mechanism that we used for injecting the environment variables in the front-end: a generated script like env.js. For one, this is less efficient than generating a bundle that already has this variable replaced wherever it needs to be replaced. The other is that a global variable is normally frowned upon, since it can clash with global variables from other scripts or packages.
Fortunately, there is an option. We will not be using this mechanism for injecting environment variables, but I have discussed it here so that it gives you an option to try out and adopt if convenient.
Although this approach works quite well, it has the downside of having to create different bundles or builds for different environments. It also means that once deployed, a change to the server configuration, for example, cannot be done without making another build. For these reasons, I have chosen to stick with the runtime environment injection via env.js for the Issue Tracker application.
Although Webpack does all that’s necessary, such as minifying the output JavaScript when the mode is specified as production, there are two things that need special attention from the developer.
The first thing to be concerned about is the bundle size. At the end of this chapter, the third-party libraries are not many, and the size of the vendor bundle is around 200KB in production mode. This is not big at all. But as we add more features, we’ll be using more libraries and the bundle size is bound to increase. As we progress along in the next few chapters, you will soon find that when compiling for production, Webpack starts showing a warning that the bundle size for vendor.bundle.js is too big and that this can affect performance. Further, there will also be a warning that the combined size for all assets required for the entry point app is too large.
The remedy to these issues depends on the kind of application. For applications that are frequently used by users such as the Issue Tracker application, the bundle size is not of much concern because it will be cached by the user’s browser. Except for the first time, the bundles will not be fetched unless they have changed. Since we have separated the application bundle from the libraries, we’ve more or less ensured that most of the JavaScript code, which is part of the vendor bundle, does not change and hence need not be fetched frequently. Thus, the Webpack warning can be ignored.
But for applications where there are many infrequent users, most of them visiting the web application for the first time, or after a long time, the browser cache will have no effect. To optimize the page load time for such applications, it’s important to not just split the bundles into smaller pieces, but also to load the bundles only when required using a strategy called lazy loading. The actual steps to split and load the code to improve performance depends on the way the application is used. For example, it would be pointless to postpone loading of the React libraries upfront because without this, any page’s contents will not be shown. But in later chapters you will find that this is not true, when the pages are constructed using server rendering and React can indeed be lazy loaded.
For the Issue Tracker application, we’ll assume that it’s a frequently used application and therefore the browser cache will work great for us. If your project’s needs are different, you will find the Webpack documentation on code splitting ( https://webpack.js.org/guides/code-splitting/ ) and lazy loading ( https://webpack.js.org/guides/lazy-loading/ ) useful.
The other thing to be concerned about is browser caching, especially when you don’t want it to cache the JavaScript bundle. This happens when the application code has changed and the version in the user’s browser’s cache is the wrong one. Most modern browsers handle this quite well, by checking with the server if the bundle has changed. But older browsers, especially Internet Explorer, aggressively cache script files. The only way around this is to change the name of the script file if the contents have changed.
This is addressed in Webpack by using content hashes as part of the bundle’s name, as described in the guide for caching in the Webpack documentation at https://webpack.js.org/guides/caching/ . Note that since the script names are generated, you will also need to generate index.html itself to contain the generated script names. This too is facilitated by a plugin called HTMLWebpackPlugin.
We won’t be using this for the Issue Tracker application, but you can read more about how to do this in the Output Management guide of Webpack ( https://webpack.js.org/guides/output-management/ ) and the documentation of HTMLWebpackPlugin itself starting at https://webpack.js.org/plugins/html-webpack-plugin/ .
Continuing in the spirit of coding hygiene in the previous chapter, we modularized the code in this chapter. Since JavaScript was not originally designed for modularity, we needed the tool Webpack to put together and generate a few bundles from a handful of small JavaScript files and React components.
We removed the dependency on the CDN for runtime libraries such as React and the polyfills. Again, Webpack helped resolve dependencies and create bundles for these. You also saw how Webpack’s HMR helped us increase productivity by efficiently replacing modules in the browser. You then learned about source maps that help in debugging.
In the next chapter, we’ll come back to adding features. We’ll explore an important concept of client-side routing that will allow us to show different components or pages and navigate between them in a seamless manner, even though the application will continue to be single page application (SPA) practically.
No, Webpack does not rebuild if you save a file with just an extra space. This is because the preprocessing or loader stage produces a normalized JavaScript, which is no different from the original. A rebundling is triggered only if the normalized script is different.
As of now, we have only one page to display, the Issue List. Going forward, we’ll have other pages to render, for example, a page to edit the issue, maybe another to list all users, yet another to show one’s profile information, and so on. The App.jsx file will then need to mount different components based on user interaction. Thus, it’s convenient to keep the application separate from each top-level component that may be loaded.
Not using the default keyword has the effect of exporting the class as a property (rather than itself) of the object that’s exported. It is equivalent to doing this after defining the exportable element:
Note the destructuring assignment around the LHS. This allows multiple elements to be exported from a single file, each element you want from the import being separated by a comma. When only a single element is being exported, it’s easiest to use the default keyword.
There are many logs in the browser’s console that tell you that HMR is being invoked. Further, if you look at the network requests, you will find that for a browser refresh, requests are made to all of the assets. Take a look at the sizes of these assets. Typically, vendor.bundle.js is not fetched again when client-side code changes (it would return a 304 response), but app.bundle.js will be reloaded.
But when HMR succeeds, you will see that the entire assets are not fetched; instead, incremental files, much smaller than app.bundle.js, are being transferred.
Now that we’ve organized the project and added development tools for productivity, let’s get back to adding more features to the Issue Tracker.
In this chapter, we’ll explore the concept of routing, or handling multiple pages that we may need to display. Even in a single-page application (SPA), there are in fact multiple logical pages (or views) within the application. It’s just that the page load happens only the first time from the server. After that, each of the other views is displayed by manipulating or changing the DOM rather than fetching the entire page from the server.
The user can use the forward/back buttons of the browser to navigate between visited pages (actually, views) of the application.
Individual pages can be bookmarked and visited later.
Links to views can be shared with others. Say you want to ask someone to help you with an issue, and you want to send them the link that displayed the issue. Emailing them the link is far easier and more convenient for the recipient than asking them to navigate through the user interface.
Before SPAs really matured, this was rather difficult, or sometimes impossible. SPAs had just a single page, which meant a single URL. All navigation had to be interactive: the user had to go through the application via predefined steps. For example, a link to a specific issue couldn’t be sent to someone. Instead, they had to be told to follow a sequence of steps on the SPA to reach that issue. But modern SPAs handle this gracefully.
In this chapter, we’ll explore how to use React Router to ease the task of setting up navigations between views. We’ll start with another view to the application, one where the user can see and edit a single issue. Then, we’ll create links between the views so that the user can navigate between them. On the hyperlinks that we create, we’ll add parameters that can be passed to the different views, for example, the ID of the issue that needs to be shown, to a view that shows a single issue. Finally, we’ll see how to nest components and routes.
Hash-based : This uses the anchor portion of the URL (everything following the #). This method is natural in the sense that the # portion can be interpreted as a location within the page, and there is only one page in an SPA. This location then determines what section of the page is displayed. The portion before the # never changes from the one and only page (index.html) that comprises the entire application. This is simple to understand and works well for most applications. In fact, implementing hash-based routing ourselves, without using a routing library, is quite simple. But we won’t do it ourselves, we will use React Router to do that.
Browser history: This uses a new HTML5 API that lets JavaScript handle the page transitions, at the same time preventing the browser from reloading the page when the URL changes. This is a little more complex to implement even with help from React Router (because it forces you to think about what happens when the server gets a request to the different URLs). But it’s quite handy when we want to render a complete page from the server itself, especially to let search engine crawlers get the content of the pages and index them.
We’ll start with the hash-based technique because it’s easy to understand, and then switch over to the browser history technique because we’ll implement server-side rendering in a later chapter.
Let’s now split the application’s main page into two sections: a header section containing a navigation bar with hyperlinks to different views, and a contents section, which will switch between the two views depending on the hyperlink selected. The navigation bar will remain regardless of the view that is shown. In the future, we may have other views in the contents section. Let’s create a component for the contents and place it in a file called Contents.jsx under the ui/src directory. This component will be responsible for switching between views.
The four routes need to be enclosed in a wrapper component, which can be just a <div>. But to indicate that only one of these components needs to be shown, they should be enclosed in a <Switch> component so that only the first match’s component will be rendered. In this case, we do need the switch because the last route will match any path.
Note also that the match is a prefix match. For example the path / will match not only / but also /issues and /report. Therefore, the order of routes is also important. The Redirect has to come after /issues and /report, and the catch-all route will need to come last. Alternatively, the exact property can be added to any route to indicate that it needs to be an exact match.
Note that the matching is not like the Express router in two ways. Firstly, in Express, the match is exact by default, and an * has to be added to match anything that follows. Secondly, in Express, a route match stops further processing (unless it’s middleware, which can add value to the request-response process and continue), whereas in React Router, a <Switch> is explicitly needed to make it stop at the first match. Otherwise, all components whose routes match will be rendered.
As for the navigation bar, we’ll need a series of hyperlinks. Since we are going to use the HashRouter , the pages will all have the main URL as /, followed by an in-page anchor that starts with the # symbol and has the actual path of the route. For example, to match the /issues path as specified in the Route specification, the URL will be /#/issues, where the first / is the one and only page of the SPA, # is the delimiter for the anchor, and /issues is the path of the route.
Although no effort has been spared to ensure that all code listings are accurate, there may be typos or even corrections that did not make it into the book before it went to press. So, always rely on the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ) as the tested and up-to-date source for all code listings, especially if something does not work as expected.

Navigation bar and usual issue list

The issue report placeholder
If you type any other text instead of report or issues, you should see the Page Not Found message as well. Importantly, you should be able to navigate using the forward and back buttons through the history of your navigation. A refresh on the browser should also show the current page.
Remove exact from the Redirect’s list of properties. What happens? Can you explain the behavior? Can you achieve what is required without the exact property? (Remember to revert the change after this exercise.)
Replace <Switch> with a <div>. Now what happens? Can you explain this as well? (Revert the code to restore the original behavior after this exercise.)
When viewing the issue list, if you append some extra text to the URL, for example, /#/issues000, what do you expect to happen? Try it out to confirm your answer. Now, try the same but with a / before the extra text, for example, /#/issues/000. Now what do you expect, and what do you see? Also try adding exact to the route. What does it tell you about the matching algorithm?
Answers are available at the end of the chapter.
As you just saw (that is, if you had done the exercise in the previous section), the URL’s path and the route’s path need not be a perfect match. Whatever follows the matched part in the URL is the dynamic part of the path, and it can be accessed as a variable in the routed component. This is one way of supplying parameters to the component. The other way is using the URL’s query string, which we will explore in the next section.
Let’s use this facility to show a page that lets us edit an issue. For now, we’ll create a placeholder, just like we did for the report. We’ll call this file IssueEdit.jsx . Later, we’ll make an Ajax call and fetch the details of the issue and show them as a form for the user to make changes and save. Just to ensure that we are receiving the right ID of the issue, let’s display it in the placeholder.

The Edit page placeholder
It’s quite simple and natural to add variables such as the ID of the issue being edited as route parameters like we saw in the previous section. But there will be cases where the variables are many and not necessarily in some order.
Let’s take the case of the issue list. Until now, we were showing all the issues in the database. This is obviously not a great idea. Ideally, we will have many ways of filtering the issues to be shown. For example, we’d like to filter on status, assignee etc., be able to search the database for issues containing certain text, and have additional parameters for sorting and paginating the list. The Query String part of the URL is an ideal way to deal with these issues.
You should get a response that contains only new issues. Further, you can ensure that the original query (without any filter) also works correctly, returning all issues in the database.
At this point in time, if you try the application and apply a filter by clicking on each of the filter hyperlinks, you’ll find that the list of issues doesn’t change. But on a browser refresh with an existing filter in the URL, it does show the correct filtered list of issues. You’ll also find that navigating to another route, for example, the report page or the edit page, and using the back button makes the filter take effect. This indicates that loadData() is being called on the initial render, but a change in the query string doesn’t cause loadData() to be called.
I talked briefly about component lifecycle methods earlier. These are hooks into various changes that React does on the component. We used the lifecycle method componentDidMount() to hook into the initially ready state of the component. Similarly, we need to hook into a method that tells us that the query string has changed, so that we can reload the list. The complete set of lifecycle methods is very nicely described in this diagram: http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/ .
From the diagram, it is clear that the lifecycle method componentDidUpdate() is good for taking action whenever some properties change. A render() is automatically called when this happens, but that’s not enough. We also need to refresh the state by making a call to loadData().
Now, if you navigate between the different filter hyperlinks, you will find that the issue list refreshes based on the filter selected.
Navigate between two different issues’ edit pages, by changing the Issue ID in the URL bar, or by creating a bookmark for one of them. The two different pages are shown correctly. Compare this with the Issue List page, where a componentDidUpdate() method was needed. Why is this so? Hint: Think about how a change in properties affected the issue list vis-à-vis the Issue Edit page.
Within componentDidUpdate(), there is a check for old and new properties being the same. Is this needed? One reason, is of course, that it avoids an unnecessary loadData(). But why not just reload the data when properties change? Try it out and see for yourself. (Remember to revert the changes after the exercise.)
Add a breakpoint in the render() method of IssueAdd and switch between filters. You will find that this component is being rendered again when the filter changes. What are the performance implications? How can this be optimized? Hint: Read the “Component Lifecycle” section in the documentation on React at https://reactjs.org/docs/react-component.html .
Answers are available at the end of the chapter.
The paths in a Link are always absolute; it does not support relative paths.
The query string and the hash can be supplied as separate properties to the Link.
A variation of Link, NavLink is capable of figuring out if the current URL matches the link and adds a class to the link to show it as active.
A Link works the same between different kinds of routers, that is, the different ways of specifying the route (using the # character, or using the path as is) are hidden from the programmer.
We can take up IssueFilter next, where there are query strings. This time, instead of a string, let’s supply an object containing the path and the query string separately. The object properties for these are pathname and search, respectively. Thus, for the link to new issues, the to property will contain the pathname as /issues, and the query string as ?status=New.

The Issue List link highlighted when viewing the list
If you click on the same link twice, you may also see a warning in the Developer Tools console saying “Hash history cannot PUSH the same path...” This message is seen only in development mode and is shown just in case we are programmatically pushing in a route path that is the same as before. You can safely ignore this warning. In any case, we’ll be transitioning to the browser history router soon, where this warning will not be seen.
You’d have noticed that we did not use NavLinks for the filter links. Try changing these also to NavLinks. What do you observe when you navigate between the filters? Can you explain this? (Remember to revert the changes after you are done with the experiment.)
Let’s say you are using a third-party CSS library, and the way to highlight a link using this library is to add the current class rather than the active class. How would you do this? Hint: Look up the documentation of NavLink at https://reacttraining.com/react-router/web/api/NavLink .
Answers are available at the end of the chapter.
Query strings are typically used when the variables’ values are dynamic and could have many combinations that cannot be predetermined. They are also typically a result of an HTML form. A form would require that the query string be constructed dynamically, as opposed to a predetermined string that we used in the Links until now.
In later chapters, we’ll be creating a more formal form with more than just the status as a filter, but in this section, we’ll add a simple dropdown and set the query string based on the value of the dropdown. We could directly reload the list, by passing a callback from IssueList that takes in the new filter as a parameter. But then, the URL will not reflect the current state of the page, and that’s not a good idea, since if the user refreshes the browser, the filter will be cleared. It’s recommended to keep the data flow unidirectional: when the dropdown value changes, it changes the URL’s query string, which in turn applies the filter. The same would work even if we started in the middle: change the URL’s query string directly, and it will apply the filter.
The compiler strips all whitespaces within JSX at element boundaries, so a space after the label Status: will have no effect. One way to add a space after the label is using the HTML non-breaking space using . Another way to insert an element is to add it as JavaScript text, and that’s what we’ve used in this example.

The issue list filtered using a dropdown
We used the push() method of history . What other method could have been used, and what would be the difference in the effect? Hint: Look up the documentation of history at https://reacttraining.com/react-router/web/api/history . Try it out. (Remember to revert the changes after the exercise.)
Filter the issue list on say, New. Now, keep the developer console open and click refresh in the browser. Does the dropdown reflect the status of the filter? Select New in the dropdown again. What do you see? What does it mean?
The IssueList component has access to the history object. So, instead of using withRouter on IssueFilter, you could either pass the history object from IssueList to IssueFilter, or pass a callback to IssueFilter that sets a new filter and call that from the child component. Compare these alternatives. What are the pros and cons as compared to the original method using withRouter?
Answers are available at the end of the chapter.
A common pattern for displaying details of one object while displaying a list of objects is using the header-detail UI pattern. This is the same as some email clients, notably Outlook and Gmail with a vertical or horizontal split use. A list of objects is shown with brief information about them (the sender and subject of each email), and on selecting one of them, further detail of the selected object (the message itself) is shown in a detail area.
An Issue Tracker is not of much use unless it had the ability to store a detailed description and comments by various users along with each issue. So, similar to the email clients, let’s add a description field to an issue, which can be lengthy text and not suitable for displaying within the table of issues. Let’s also make it so that on selecting an issue, the lower half of the page shows the description of the issue.
This calls for nested routes, wherein the beginning part of the path depicts one section of a page, and based on interaction within that page, the latter part of the path depicts variation, or further definition of what’s shown additionally in the page. In the case of the Issue Tracker application, we will have /issues showing a list of issues (and no detail), and /issues/1 showing the detail section with the description of issue with ID 1, in addition to the issue list.
Thus, unlike Express routes, React Router’s routes don’t all need to be predeclared; they can be placed at any level and are evaluated during the process of rendering.
You may have to start from the Home link if you ran the script, since it may have removed some issues that you had created manually. Otherwise, if the UI is referring to these issues, you’ll probably get a GraphQL error to the effect that Query.issue cannot be null.
We’ll maintain the state, which will contain an issue object.
The ID of the issue object will be retrieved from match.params.id like in the IssueEdit component.
The issue object will be fetched using the issue GraphQL query using the fetch() API, in a method called loadData(), and set as the state.
The method loadData() will be called after the component mounted (for the first time), or when the ID changes (in componentDidUpdate()).
In the render() method, we’ll just display the description using the <pre> tag so that newlines are maintained in the display.
To integrate the IssueDetail component into the IssueList component, we’ll need to add a route, as discussed in the beginning of this section. But, instead of hardcoding /issues, let’s use the path as matched in the parent component, using this.props.match.path. This is so that even if the parent path changes for any reason, the change is isolated to one place.
To select an issue, let’s create another link beside the Edit link in the table of issues. This time, let’s use a NavLink to highlight the selected issue. Ideally, we should be able to select by clicking anywhere in the row, and it should be highlighting the entire row when selected. But let’s keep that for a later chapter, where we will have better tools to implement this effect. The NavLink will point to /issues/<id>, where <id> is the ID of the issue in the row being selected.
Further, to not lose the query string part of the URL, we’ll have to add the current query string as the search property to the target of the link. But, to access the current query string, we’ll need access to the current location, and since IssueRow is not displayed as part of a Route, we’ll have to inject the location by wrapping the component with withRouter .
If you now test the application, you’ll find a Select link next to the Edit link for every issue. Clicking on this link should change the URL so that the ID of the issue is appended to the main path, but before the query string (if any). You should try this with and without filters to make sure it works in both cases, and that a refresh continues to show the description of a selected issue.

The selected issue and description
Instead of using a Route while rendering IssueList , we could have defined the issue list’s route’s path as /issues/:id, and then displayed the IssueDetail passing the ID as part of props. Compare the two methods of achieving the same result. What are the pros and cons?
Answers are available at the end of the chapter.
We discussed the two kinds of routing—hash-based and browser history-based—at the beginning of this chapter. Hash-based routing was easy to understand and implement if we were to do it ourselves: just changing the anchor part of the URL on transitions would suffice. Also, the server had to return only index.html for a request to / and nothing else.
But the downside of using hash-based routing is when the server needs to respond to different URL paths. Imagine clicking refresh on the browser. When hash-based routing is used, the browser makes a request to / from the server, regardless of what follows the # or the actual routing path. If we had to let the server handle this refresh differently, a better strategy would be to use a different URL base (that is, without the # and what follows it) for different routes.
This need (responding differently to different routes from the server itself) arises when we need to support responses to search engine crawlers. That’s because, for every link found by the crawler, a new request is made provided the base URL is different. If what follows the # is different, the crawler assumes that it’s just an anchor within the page, and only a request to / is made regardless of the route’s path.
To make our application search engine friendly, using browser history-based routing is necessary. But that’s not all, the server also has to respond with the entire page. In contrast, for a browser request, the page would have been constructed on the browser. We’ll not be generating the page to be displayed just yet, because it’s fairly complex to achieve that and it deserves a chapter of its own. For now, we’ll just switch over to the browser history-based router, but assume that the page is constructed by manipulating the DOM only.
The switch to using this new router is as simple as changing the import statement and using BrowserRouter instead of HashRouter. This component achieves routing by using the HTML5 history API (pushState, replaceState, and popState) to keep the UI in sync with the URL.
To test this change, you have to start from the home location, that is, http://localhost:8000. The application will seem to work, all the links will lead you to the right pages and views. Also, you’ll find that the URLs will be without a #, and simple URLs like http://localhost:8000/issues for the Issue List page.
That’s because the URL in the browser is currently pointing to /issues and the browser makes a request to the server for /issues, which is not handled by the UI server. To address this, we need to make a change to the UI server, which returns index.html for any URL that is not handled otherwise. This can be achieved by installing an Express route after all the other routes for the path * which reads the contents of index.html and returns it.
If you test the application after making this change, you will find that a refresh on any page works as before. It would also be a good idea to test that other files in the public directory are being served correctly, notably, app.bundle.js and vendor.bundle.js.
But in normal development mode, HMR would serve these bundles rather than let the UI server pick them up from the public directory. So, you will need to disable HMR (by setting the environment variable ENABLE_HMR to false), compile the bundles manually using npm run compile, and then start the UI server. Then, on refreshing the application, you should see these files correctly retrieved from the server. Do not forget to revert the changes to HMR after you are done with the test.
There is still one change left to be done that affects the functioning of HMR. Webpack has a configuration option called publicPath under output. This is an important option when using on-demand-loading or loading external resources like images, files, etc. But we have not used these thus far and not having set this to any value did not affect the functioning of the application. The value defaults to an empty string, which means the same location as the current page.
It turns out that Webpack uses the value of publicPath to fetch update information for modules when they change and are recompiled by HMR. Thus, if you change a source file while you are at a location such as /edit/1 or /issues/1, you will find that HMR calls fail. If you look at the Network tab of developer tools, you’ll find that these requests return the contents of index.html instead of the module update information.
You could compare the two requests and responses by looking at what happens when a source file is changed, while you have the browser pointed to /issues and then /issues/1. In the first case, you’ll see a request to a resource like /f3f397176a7b9c3237cf.hot-update.json, which succeeds. Whereas in the second case, it will be like /edit/f3f397176a7b9c3237cf.hot-update.json, which fails. That’s because Webpack is making a request relative to the current location. This request could not be matched by the hot middleware, so it fell through to the catch-all Express route, which returned the contents of index.html .
After this change, you’ll find that HMR works fine from any page in the application.
If we had used hrefs instead of Links for the hyperlinks in the application, would the transition to use BrowserRouter have been as simple? What other changes would have had to be done?
Let’s now compare using an href and a Link based on their effect. Add an href in addition to the Link for navigating to Edit in the Issue table. Click on these two links and compare what happens. (Hint: Use the Developer Tools’ Network tab to inspect network traffic.)
Now, do the same comparison with HashHistory (Note: You have to use /#/edit in the href, /edit will not work.) Now, is there a difference? Try to explain what you see. (Remember to revert the experimental changes after the exercise.)
Answers are available at the end of the chapter
In this chapter, you learned how to implement client-side routing, that is, show different pages depending on links in a menu or a navigation bar. The React Router library helped with this.
You also learned how to connect the URL in the browser with what is shown in the page, and how parameters and query strings can be used to tweak the page contents. Implementing routing correctly is key to giving the user a natural feel when clicking on hyperlinks and using the back/forward buttons in the browser. Further, connecting the URL in the browser to the contents on the page not only helps us think about different pages or views in an organized manner, but also helps the user to bookmark links and use the browser’s refresh button.
In the next chapter, we’ll explore how to deal with a very common occurrence in enterprise applications: forms, the React way. As we do that, we’ll also complete the CRUD for the Issues object by implementing Update and Delete operations for issues.
If the exact property is removed from the Redirect component, you’ll see that the contents section is blank, regardless of the hyperlink that is clicked. This is because all URLs now match the first route. Since there is no component defined for the route, the page is blank. Further, you’ll also see an error in the console saying that you’re trying to redirect to the same route. This is because even on navigation, the same route (the Redirect) is the one that matches.
You can almost achieve the required behavior by reordering the routes: the redirect can be placed after the two routes and before the catch-all. Now, the /issues and /report paths will match the first two routes and stop there. If neither of them match, then any other route will match / and will redirect to /issues. This is not exactly the same behavior as before, because instead of showing Page Not Found, it will always redirect to /issues.
If you replace <Switch> with a <div>, you’ll find that the Page Not Found message is always shown in addition to the issue list or the report placeholder. This is because the matching does not stop at the first match, but instead shows all the components from the routes that matched. The NotFound component’s path (empty) matches any path, and thus shows always.
The URL /#/issues000 shows a Page Not Found, whereas /#/issues/000 shows the issue list without an exact property, and Page Not Found otherwise. This demonstrates that a non-exact route matches full segments of the path, each segment being separated by a /. It is not a simple prefix match.
When properties are changed in a component, a render() is automatically called by React. When a change in properties only affects rendering, we don’t need to do anything further.
The difference in the issue list was that the change in properties caused a change in the state. This change had to be triggered somewhere, and we chose the lifecycle method componentDidUpdate() to do that. Eventually, even in Issue Edit page, when we load the issue details in an asynchronous call to the server, we will have to implement the componentDidUpdate() method.
Without the check for old and new properties being the same, it will result in an endless loop. This is because a new state is also considered an update to the component, thus calling componentDidUpdate() again and the cycle will continue endlessly.
Any change in a parent component will trigger a render in the child, because it is assumed that the state of the parent can affect the child as well. Normally, this is not a performance problem, because it is only the virtual DOM that is recalculated. Since the old and the new virtual DOMs will be identical, the actual DOM will not be updated.
Updates to the virtual DOM are not that expensive, because they are no more than data structures in memory. But, in rare cases, especially when the component hierarchy is very deep and the number of components affected is very large, just the act of updating the virtual DOM may take some time. This can be optimized by hooking into the lifecycle method shouldComponentUpdate() and determining if a render is warranted.
If you use NavLinks, you will observe that all links are always highlighted. That’s because Link only matches the paths of the URL and the link, and does not consider the query string as part of the path. Since the path is /issues for all the links, they will always match.
Compared to route parameters, query parameters aren’t expected to be a finite set, and therefore, not encouraged for use for navigation links. If the filters were navigation links, we should use route parameters as we did for the main navigation bar.
The activeClassName property for the NavLink component determines the class that is added when the link is active. You could set this property to the current value to have the desired effect.
The history.replace() method could have been used, which replaces the current URL so that the history does not have the old location. On the other hand, router.push() ensures that the users can use the back button to go back to the previous view.
Replacing can be used when the two routes are not really different. It is analogous to a HTTP redirect, where the contents of the request are the same, but available at a different location. In this case, remembering the first location as part of the browser’s history is not useful.
On a refresh, the dropdown resets to the default value, All. But the list is filtered on the previous selection of the dropdown, and this is reflected in the URL as part of the query string. We’ll synchronize the dropdown value and the query string when we discuss forms in the next chapter.
If the dropdown value is changed to select the original status, the developer console shows a warning:
The wrapper function withRouter is a bit hard to understand. The other options are easily understood, and may even seem simpler. But imagine a more nested hierarchy where IssueFilter is more than one level inside IssueList. In such a case, the history object will have to be passed through all intermediate components, increasing the coupling among all these components.
Letting IssueFilter directly manipulate the URL reduces coupling and lets each component deal with a single responsibility. For IssueFilter, it is one of setting the URL, and for IssueList, it is one of using the query string from the URL, regardless of how it was set.
The difference between the two methods is not much, and could also be a matter of consistency. All Route does anyway is match the URL and display a component if it matches. Since the match has happened as part of IssueList, the nested route does not add much, at least in this case. So, displaying the IssueDetail component wrapped in an if condition (on the presence of an ID) will work just fine.
Another consideration is how deeply nested in the hierarchy the child component is. In case of IssueDetail, it was just one level deep, and passing in the ID from IssueList to IssueDetail was straightforward. If the nested-routed component is very deeply nested, then the ID would have to be passed through multiple other components, so it’s perhaps easier for the IssueDetail to get this parameter from the URL via the router itself.
If we had not used Links, we would have had to change all the hrefs to remove the #/ prefix. This is one advantage of using Links as compared to plain hrefs.
When using BrowserHistory, an href causes the browser to navigate to another URL, thus initiating a request to the server, fetching a page and then rendering it. In comparison, a Link does not cause a new request to the server; it simply changes the URL in the browser and deals with the change programmatically, by replacing the component that needs to be displayed for the new route.
When using HashHistory, the two methods are not visibly different because the base URL is always the same (/). The browser does not make a new request to the server even when clicking on the href because the base URL does not change.
User input is an important part of any web application, and the Issue Tracker application is no different. We created a form and user input to create a new issue. But it was very rudimentary, and it did not demonstrate how forms are supposed to be dealt with in React.
In this chapter, we’ll start taking in a lot more user input. We will convert the hard-coded filter to something more flexible with user input, and then we’ll fill in the Edit page with a form. Finally, we’ll add the ability to delete issues from the Issue List page, though this is not necessarily a form.
To be able to do all this, we’ll also have to modify the back-end APIs to support these functions. We’ll modify the List API to take more filter options, and we’ll create new Update and Delete APIs. We’ll thus complete implementing all of the CRUD operations.
One of the challenges that a declarative style of programming faces is user interaction in form inputs, especially if they contain a value that is used to display the initial value from the model. If, like, conventional HTML code, the value of a text <input> is set to a string, it means that the value is always that string because it was declared so. On the other hand, if user edits were allowed to change that value, any re-rendering destroys user changes.
We did not have this problem with the IssueAdd component and form because the form did not have any initial values, that is, it performed the function of only taking in user input. So, the internal state of the component could be left uncontrolled by its parent, the IssueAdd component. Whenever the value of the input is required, for example when the user clicks the Add button, it can be determined by looking at its value using conventional HTML functions.
To be able to show a value in the input, it has to be controlled by the parent via a state variable or props variable. This is done simply by setting the value of the input to that state or props variable. Thus, the input will directly reflect that value, and the React component that renders the form will also control what happens in that form on subsequent user input. An input form element whose value is controlled by React in this way is called a controlled component.
At this point, if you test the application, you will find that, unlike before, a refresh shows the current value of the filter rather than the default value All.
At this point in time (you may ignore the ESLint errors, because we’ll soon fill in the required code for this method), if you test the application, you will find that you cannot change the value of the dropdown! This is because the value of the select is still the original value. Until that value is changed, the dropdown cannot show a new status.
The solution many other frameworks (for example, Angular) offer is two-way binding out of the box. The components are not just bound to the value in the state, but also vice versa. Any user input automatically changes the state variable too.
But in React, unidirectional data flow is important, and it does not support two-way binding as part of the library. In order to let the user’s changes flow back into the form input component, the new value has to set in the input. To get hold of the new value, the onChange() event has to be trapped, which will have the event as an argument, and as part of the event, we can get the new value that the user has selected.
This also means that rather than the URL Parameters, we need a store for the input’s value, which can be changed to reflect the new value in the dropdown. The state is the ideal place to store this value. When the user changes the value, the state variable can be updated with the new value using setState() within the onChange() event handler so that it is reflected back as the value for the input.
At this point, you will find that the Apply button works by changing the URL and thus getting a new filter to be applied. But there is still one minor issue. When a filter is applied and the filter is changed via a Link, for example by clicking on Issue List in the navigation bar, the new filter is not reflected. This is because when the link is clicked, only the props for the component are changed. The component is not constructed again and the state is not modified.
Although no effort has been spared to ensure that all code listings are accurate, there may be typos or even corrections that did not make it into the book before it went to press. So, always rely on the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ) as the tested and up-to-date source for all code listings, especially if something does not work as expected.

The new filter form
Now that we have a form for the filter, we are in a position to add more ways to filter the list of issues. A useful filter in the real application would be one on the Assignee field. But this is not so interesting from the point of view of learning about forms, as it is a text field and quite straightforward—we’d have to add a text input, and in its onChange, we’d have to update a state variable and use that in the filter.
A more interesting field to filter on would be a non-text field, which is not so straightforward. So, let’s add a filter on the Effort field, as that is a number. We’ll need two fields for this, a minimum and a maximum value to filter on, both of which are optional. To start, let’s change the API to implement this filter and test it using the Playground.
You can give various values for effortMin and effortMax in the Query Variables section at the bottom to test it.
Add a few issues using the Add form in the Issue Tracker application. Now, if you run a query in the Playground with effortMin as 0, you will find that the added documents are not returned. This is true for any value of effortMin. Why?
How would you modify the filter if you wanted all documents with an undefined effort to be returned regardless of the query? Hint: Look up the MongoDB $or query operator at https://docs.mongodb.com/manual/reference/operator/query/or .
Answers are available at the end of the chapter.
In this section, we will change the UI to add two inputs for the effort filter. Since these two inputs need to accept only numbers, we’ll add a filter to the user’s keystrokes so that only numbers are accepted.
At the same time, let’s also add these in showOriginalFilter, which is a similar change. (The changes are trivial and not highlighted here for brevity. Refer to Listing 10-6 if needed.) Note that we are using strings in the state to represent these values, which are actually numbers. The convenience it gives us is that we don’t need to convert between number and strings when manipulating or reading from the URL parameters.
Let’s add a similar handler for onChangeEffortMax and bind these methods to this in the constructor. (Refer to Listing 10-6 for this simple change.)

The issue list showing effort filters
Let’s say we don’t convert the values of effortMin and effortMax to integers, that is, we use the string values as is in the GraphQL query variables. What do you expect to happen? Try it and confirm your answer.
Try using <input type="number"> rather than the default text type. Test it on different browsers, say, Chrome, Safari, and Firefox. Type possibly valid number characters such as . (dot) and E. What do you observe? Why? Hint: Add some console.log statements within the change handler and observe the logs.
Answers are available at the end of the chapter.
We had a placeholder for the Edit page all this while. Now that you’ve learned about components, especially controlled components, let’s try to create a complete form for the Edit page in IssueEdit.jsx, displaying all the fields of an issue in input fields that can be changed by the user. We’ll also have a Submit button, but we will not handle the submission of the form yet. We’ll leave that for the following sections, after we have implemented an API to update an existing issue.
If these two conditions did not match, we can render the form. Note that we used the double-equals rather than the triple-equals, which matches anything that seems like null, which includes undefined. We’ll use a table with two columns to show a label for the field name in the first column, and the input (or a read-only label for ID and created date) as the value. The form will need a submit button in addition to all the fields, and a submit handler, which we’ll name handleSubmit().
For the rest of the input fields, refer to Listing 10-7.
We used the ES2015+ spread operator ... to spread the values of the issue object as if they were all mentioned individually, like { id: prevState.issue.id, title: prevState.issue.title }, etc. This is a simpler way of copying an object compared to Object.assign(). Then the property with name as the value of the variable name is used to override the properties that were expanded.

The Edit page
What happens if we don’t convert a null value in a string field to an empty string? Try this yourself by removing the line that checks for null and assigns an empty string for the description field. Do this on an issue that has the description field missing rather than a value, even an empty string.
Answers are available at the end of the chapter.
When dealing with non-string data types, when it is required that the value is validated (for example, check if the completion date is not before today), it has to be converted to the natural data type. The same conversion is needed before sending the modified issue to the server.
If there is more than one input of the same type (number or date), the conversions for each input need to be repeated.
The inputs let the user type anything and don’t reject invalid numbers or dates. We already found that the HTML5 input types aren’t much help, and since the onChange handler is a common one, you can’t add masks there for different input types.
Ideally, we want the form’s state to store the fields in their natural data types (number, date, etc.). We also want all of the data type conversion routines to be shared. A good solution to all this is to make reusable UI components for the non-string inputs, which emit natural data types in their onChange handlers. We could very well use some of the great packages like react-numeric-input and react-datepicker that provide these UI components. But for the sake of understanding how these UI components can be built, let’s create our own, minimalistic components.
We’ll first create a simple UI component for the number with simple validation and conversion. Then, we’ll create a more sophisticated UI component for the date, which does more, like letting the user know if and when the value is invalid.
In all these components, we’ll take the approach of a disjoint state—where the component is a controlled one as long as the user is not editing the component and its only function is to display the current value. When the user starts editing, we’ll make it an uncontrolled component. In this state, the value in the parent will not be updated, and the two values (current and edited) will be disjointed. Once the user has finished editing, the two values will be brought back in sync, provided the value is valid.
Another way to look at this is that the specialized component is uncontrolled, but the actual <input> element is controlled. That is, within the specialized component, we’ll have a state variable that controls the value in the input element. This approach helps us deal with temporarily invalid values, which in many cases are needed when transitioning from one valid value to another. The need may not be that apparent with simple numbers. But when you have to deal with data types like decimals and dates, there are situations where the user has not finished typing and the intermediate value is invalid. It helps a lot in improving usability if the user finishes typing before the input is judged to be valid or not.
The first specialized input component we’ll create is for number inputs. We’ll use this for the effort field in the Edit page in place of a plain <input> element. Let’s call this component NumInput and use a new file under ui/src/ directory called NumInput.jsx for this.
In the unformat() function , we returned null if the string doesn’t represent a number. Since we’ll check for valid characters in the user’s keystrokes, a non-numeric entry can happen only if the string is empty, so this is good enough.
In the render() method , we’ll just render an <input> element with the value set to the state’s variable and the onChange() and onBlur() handlers of the component class. These methods have to be bound to this in the constructor since these are event handlers. Further, we’ll copy over all other properties that the parent may want to supply as part of props for the actual <input> element (for example, the size property). Let’s use the spread operator to do this seamlessly, using the syntax {...this.props}.
After these changes, if you test the application, it will seem to work fine as long as you navigate from the issue list to any of the Edit pages. But if you use the Next/Prev buttons, you’ll find that the value in the effort field doesn’t change, instead, it retains the original issue’s effort value. It would remain blank if the first issue that was displayed had an empty effort.
We could hook into the lifecycle method componentWillReceiveProps() (deprecated) or getDerivedStateFromProps() to reinitialize the state. But these methods can also be called when there is no change in the props, but the parent is being re-rendered for some reason. We can check for change in the props value, but what about when the Next/Prev issue has the same effort value?
We could use the lifecycle method componentDidUpdate() to replace the state. But as the ESLint errors suggest when you try to do this, it is not a good idea to set the state synchronously in this method.
We could trap the onFocus() event to set the state for editing along. Otherwise, we can display the props value converted to a string. But even this will not work in corner cases when the issue being shown is replaced with another while the input has focus. (It can happen if the issue being displayed is changed as part of a timer like in a slideshow.)
We could redraw the page when a new issue is loaded. This can be done, for instance, by introducing a “loading” state while loadData() is in progress and rendering a message or null instead of the form. This will force a reconstruction of the component when the issue object changes. But this will cause a flicker when a new issue is loaded because the entire form disappears temporarily.
Any of these options can work with some assumptions or some workarounds. But let’s use the recommended way of dealing with the situation. What is essentially needed is a way to construct the component again with a new initial property. The best way to do this is to assign a key property to the component that changes when a new issue is loaded. React uses this property to indicate that a component object cannot be reused if the key is different; a new one has to be constructed.
Now, if you test the application, you should be able to edit the effort field and see that the value changes according to the issue object when you click on Next/Prev. Also, when you click Submit, you should be able to see that the value of effort in the issue object is indeed a number (there will be no quotes around the value) .
In the Number input specialized component, we did not have to worry about the validity of user input, because we prevented any invalid values from being typed by the user. We cannot do this with the Date input because the validity cannot be determined purely by the characters that are allowed in a date. For example, although all digits are allowed, something like 999999 is not a valid date.
Note that we allowed an empty string to be a valid date, along with any other string that could be converted to a date object using the unformat() method , which we’ll just use the Date(string) constructor for.
In the onChange() method, we’ll check for valid characters, which are just digits and the dash (-) character. All other characters will be disallowed. The regex we’ll use for this check is /^[\d-]*$/.
In the constructor and in the method loadData(), we’ll have to set the state variable invalidFields to an empty object to initialize it for any new issue that’s being loaded.
The style change will need a browser refresh because HMR does not handle changes to index.html. Once you do that, you can test the new Date input field. When you enter a valid value and click Submit, you’ll see the actual date object being stored and displayed in the console. For all invalid values, you should see a red bordered input as well as an error message in red. For these invalid inputs, you’ll see the original or any previous valid value entered by the user on clicking Submit.
A textual input may seem unnecessary because there is no validation or conversion that needs to be done. But to let a component handle null values for input fields would be quite convenient. Otherwise, for every optional text field, we’ll need to handle the null check and use an empty string while loading data.
Thus, very similar to the NumInput component, let’s create a TextInput component, with some differences. The format() and unformat() will exist simply for converting to and from null values. In the onChange() method, we’ll not have masks for valid user inputs: any input will be allowed. Lastly, to handle variations in the HTML element name (we could have textarea and input, both handling textual data), let’s not hard-code the element tag in the component, instead let’s pass it in as an optional tag property that we can default to input. To be able to do this, we will have to fall back to the React.createElement() method instead of using JSX, as the tag name is a variable.
In the IssueEdit component, we can replace all textual input elements to TextInput, and the description element to TextInput with the property for tag set to textarea. We’ll have to use the key property to ensure that the component is reconstructed when switching from editing one issue to another. Finally, we can remove all the null to empty string conversions and load the issue as is in the state of the IssueEdit component.
Now, you can test all text inputs and see if empty strings input by the user are converted to null values when clicking on Submit and vice versa: null values in the database should appear as empty strings in the UI.
Updating one or more fields in the document: This can use the MongoDB update command and use the $set operator to set the new values of fields.
Replacing the entire document with new values: This would be similar to creating a new issue, by supplying all the values (changed as well as unchanged) for the fields in the document. The MongoDB replace command can be used for replacing the document with a new one.
In the case of the Issue Tracker, the id and created fields are special, as they are initialized only when the issue is created and never modified after that. A replace approach would necessarily mean that the original object be read and merged with the new values supplied by the API’s input, otherwise the id and created fields would get new values as in the create API. The same input data type IssueInputs can be used for the replace operation as well.
If we use the update approach, where only some fields are supplied, we’ll have to maintain this list of fields that can be updated in the GraphQL schema. This data type is quite similar to the IssueInputs data type, except that all fields are optional. The downside is that a change in the list of input fields needs a change in IssueInputs and this new data type.
But there is some flexibility the update approach offers. It allows the UI to update individual fields very easily. We’ll add a feature to close an issue directly from the issue list in the following sections, and you can see how this approach supports both use cases well: replacing an issue from the Edit page as well as changing a single field from the Issue List page.
In other situations, supporting a replace operation may work better, but the presence of the created field makes supporting only the update operation more appealing. A replace in this case can be seen as an update of all modifiable fields.
Now, we can implement the actual resolver in issue.js, in a function called update() . In this function, we’ll need to validate the issue based on the new inputs. The easiest way is to fetch the full object from the database, merge the changes supplied to the API, and run the same validation that we used for adding an issue. Let’s also run the validation only if the fields that affect the validity are changing: Title, Status, or Owner. Once the validation succeeds, we can proceed to use the updateOne() MongoDB function with the $set operation to save the changes.
You can also test for invalid changes—such as a title with fewer than three characters, or a null owner when status is set to Assigned—and ensure that the changes are rejected with an error.
Now that we have a functional Update API, we can write the handleSubmit() method to make the call to the API to save the changes made by the user.
We used the ES2015+ rest operator ... to collect the rest of the values of the issue object into the changes variable after the destructuring assignment to the id and created variables.
Now, you can test the application to save any changes to the issue in the database. The changes should be seen in the Edit and Issue List pages.
Let’s now use the same API to update a single field rather than the entire issue object in one go. Let’s say we need a quick way to close an issue (that is, set its status to Closed) from the Issue List page itself.
To achieve this, we’ll need a button in every row to initiate the operation. Let’s change the IssueTable component to add this button as part of the Actions column. On click of this button, we’ll need to initiate a close action, which can be a function passed in as a callback in the props. The callback needs to be passed from IssueList via IssueTable to IssueRow. Further, to identify which issue to close, we’ll also have to receive the index of the issue in the table as another value in the props. The index can be computed in the IssueTable component itself, while iterating over the list of issues.
This set of changes can be tested by clicking on the Close button in any of the rows in the issue list. The status of the issue in that row should change to Closed .
Could we have called the update API from IssueRow itself? What are the ramifications of doing that?
Answers are available at the end of the chapter.
Now, let’s implement the Delete API’s resolver. Rather than just deleting the record for the given ID, let’s do what usually happens when a file is deleted in a computer: it is moved to trash. This is so that we have a chance to recover it at a later point in time. Let’s use a new collection called deleted_issues to store all deleted issues. We may decide to purge this table periodically, so let’s also add a deleted field to save the date and time of deletion, which can come in handy (for example, to purge all issues deleted more than 30 days old).
If the issue with ID 4 exists, it will be deleted and the API will return true. Otherwise, the API will return false. You could inspect the contents of the collection deleted_issues using the MongoDB shell to verify that the issue has been backed up in this collection.
The UI changes for deleting an issue will be quite similar to the changes we did for updating a field using the Close button.
Let’s first add the button and pass the necessary callbacks through IssueTable to IssueRows. Let’s use the name deleteIssue for the callback, which we will implement in IssueList soon. Just as with the closeIssue callback, we’ll need to delete the index of the issue. We already have the index being passed in for this purpose, so we’ll use the same here.
The next set of changes is in the IssueList component. Again, the changes are quite similar to what we did for the Close button: a deleteIssue() method takes the index of the issue to be deleted, calls the Delete API using this ID in the query variable, and removes the issue from the issues state variable if the API succeeded. If not, it reloads the data. Further, there is a possibility that the user is deleting the selected issue. In this case, let’s revert to an unselected view, that is, navigate back to /issues (i.e., without the ID suffix).
Other changes are to bind the new method to this and pass the method as a callback to the IssueTable component.

The issue list with a Delete button
We used the Edit page to explore forms and look at the difference between controlled and uncontrolled form components. We also added new APIs to cater to the needs of the new form and completed the CRUD paradigm by adding a Delete operation. Importantly, we created specialized input components that could deal with different data types that one expects in most applications.
While we did all this, a thought must have crossed your mind: can we make all this, especially the Edit page, look better in the browser? That’s exactly what we’ll set out to do in the next chapter. We’ll use a popular CSS library adapted to React to add some polish to the UI.
MongoDB is strict with respect to data types. This also means that for a field that has no value, it cannot determine the type, thus the match cannot occur if the field has a filter criterion. Any field with null values will be ignored and not returned if there is any filter on that field.
If we do need to return documents with the effort field missing, we’ll have to create a condition that includes the original filter, as well as a condition that says the effort is undefined. The $or operator takes an array of filters and matches the document against any of the filter conditions.
To match a document where the effort field is not defined, we must use {$exists: false} as the criterion for the field. Here’s an example in the mongo shell:
Even when the query is sent with strings for effortMin and/or effortMax, the server seems to accept it and automatically convert it to integers. The query works as it does when converting the effort fields to integers before sending them to the server.
Although this seems to be convenient behavior and it is tempting to not add the conversions in the UI, it is not recommended for a couple of reasons. Firstly, this behavior of the graphql-js library may change in the future and might break our implementation if and when that happens. It is safer to supply integer values as integers.
Secondly, the UI ignores any non-numeric values if the parsed value is not a number. So, the application works as if no filter were supplied. On the other hand, if the value was not parsed, the text input would be sent to the server causing an error (this can be tested by typing these non-numeric values in the browser’s URL directly).
If you set the input’s type as a number, you find that (a) it behaves differently on different browsers, (b) masking does not work on some browsers, and (c) when it does allow invalid characters, you don’t see them in onChange. This is because as per the HTML specification, when the type is specified and the input does not conform to the specification, the value of the input is supposed to return an empty string. It is also up to the browser how to deal with invalid values; for example, some browsers may display the fact that the input is invalid, whereas others may prevent an invalid entry.
When using React, it is best not to use the type attribute of input fields, instead it’s best to deal with the validation or masking yourself (or use packages that do it for you). This lets the behavior be predictable across browsers, as well as allows you to make informed decisions on what to do with invalid input, especially input that is invalid temporarily in order to get to a valid value.
If the value of a controlled component is set to null, a warning is shown on the console by React:
'value' prop on 'textarea' should not be null. Consider using an empty string to clear the component or 'undefined' for uncontrolled components.
The warning is because a null value is a signal to React that the component is uncontrolled. Controlled components must have a non-null value.
Although initiating the API can be done from the IssueRow component itself, signaling the success and updating the issue list can only be done within the IssueList component, as the state resides there. Further, this would cause the IssueRow component to stop being a pure function. It would also need a handler for the close action within the component, making it necessary to define it as a class.
Since the state resides in the IssueList component, it’s best to let the same component manipulate the state as well.
CSS frameworks like Bootstrap ( https://getbootstrap.com ) and Foundation ( https://foundation.zurb.com/ ) have changed the way people build their websites. These tools make it a lot easier to make a web application look professionally styled and be responsive (that is, make it adapt to mobile screens nicely). Of course, the downside is that these existing frameworks may not give you fine-grained customizability, and your application will look like many others. But, even if you do have the luxury or the capability to create your own end-to-end custom styles, I suggest that you start off with these frameworks. That’s because there’s a lot to learn from the patterns these frameworks use.
Since we are using React as the UI library, we need to choose something that fits into and plays well with React. I evaluated React + Foundation, Material UI, and React-Bootstrap because they appeared the most popular, based on Google searches.
React + Foundation didn’t appear to be very different from React-Bootstrap in terms of capability, but Bootstrap itself is far more popular. Material UI has an interesting CSS-in-JS and inline-style approach of styling that fits well into React’s philosophy of isolating everything needed by a component, with the component itself. But this framework is much less popular and seems to be a work in progress. And, perhaps the inline-style approach is too drastic a deviation from convention.
React-Bootstrap is a safe alternative that is built on top of the very popular Bootstrap and fits our needs (except for the lack of a date picker). I thus chose React-Bootstrap for this book. In this chapter, we’ll look at how to make the application look professionally styled using React-Bootstrap. I won’t be covering how to make custom themes and other advanced topics, but you’ll learn just enough to appreciate what React-Bootstrap is all about, so you can go further easily if and when required.
React-Bootstrap contains a library of React components and has no CSS styles or themes itself. It requires Bootstrap stylesheet to be included in the application to use these components. The version or mechanism of including the stylesheet is left to us, but the version that we need to use is Version 3. The latest version of Bootstrap (Version 4) is not yet supported by React-Bootstrap. So, let’s include Version 3 of the bootstrap stylesheet. The easiest way is to include it from a CDN directly in index.html, as recommended by the React-Bootstrap “Getting Started” page ( https://react-bootstrap.github.io ).
The next step is to include the Bootstrap stylesheet in the application. One way to do this is by using Webpack’s style and CSS loaders. This can be done using an import (or require) statement to include CSS files just like other React or JavaScript modules. Then, Webpack would build the dependency tree and include all the styles that have been imported in the bundle that is created. This is done by creating a string within the JavaScript bundle containing all the styles. When the application is loaded, the string is placed into the DOM as a <style> node.
To get this to work, we need to install the CSS and style loaders for Webpack. Then, we need to add pattern matches in the Webpack configuration that triggers these loaders based on the file extension. We will also need loaders for the icons and fonts that the stylesheet may include. Finally, we need a single import statement that imports bootstrap.css, maybe just in App.jsx.
I find that for our needs, all of this is overkill. The purpose of Webpack’s CSS and style loaders is to be able to modularize stylesheets just as we modularized the React code. If every component had its own set of styles separated into their own CSS files, this method would work great. But the fact is that Bootstrap is shipped as a monolithic stylesheet. Even if only a single component is being used, the entire CSS has to be included. So why not just include the entire stylesheet as is? That’s what we’ll do.
Alternatively, you can copy the entire dist directory under the Bootstrap library into the public directory.
At this point in time, if you test the application, you should see a different looking application because of the new bootstrap stylesheet. For example, you should see a sans-serif font (Helvetica or Arial) in place of a serif font (Times New Roman) as the default font.
Let’s now test this on a mobile device and see the effect of the Bootstrap’s responsive design. One way to do this is to actually use a mobile phone and connect to the server that’s running on your desktop. But this won’t work unless you change the environment variable UI_API_ENDPOINT to the IP address of your computer. Alternatively, you’ll have to use the proxy configuration so that all requests are routed via the UI server.

The app in a mobile emulator
As you can see, the screen looks really squished or zoomed out. You could zoom by pinching (use Shift-drag in the mobile emulator) to vary the zoom, but we’d really like it to use the smaller screen width by default. The reason the mobile browser does not take the device width is roughly like this: it assumes that the page has not been designed for mobile screens, so it picks an arbitrary width that would be appropriate for a desktop, uses that, and then zooms out the page so that it fits the screen.

The mobile emulator after the viewport setting

The Issue List page with a Bootstrap label
To get familiar with the way React-Bootstrap works with components, let’s start with a simple component: Button. Let’s replace the Apply and Reset buttons in the Issue Filter with Bootstrap buttons.
A simple text-based button can be created using the <Button> component. Apart from all the properties the regular <button> component supports, the <Button> component uses the bsStyle property to make buttons look distinct. Apart from the default, which shows the button with a white background, the allowed styles are primary, success, info, warning, danger, and link. Let’s use the primary style for the Apply button and the default for the Reset button. Thus for the Reset button, the only change is in the tag name from button to Button. The changes to the Apply button are the tag name and the addition of bsStyle="primary".
A similar change can be made to the button for the Delete action. But since the icons’ intended actions are not too obvious, it’s good to have a tooltip that is shown on hovering over the button. The HTML property title can be used for this purpose, but let’s use Bootstrap’s stylized Tooltip component. But using this is not as simple as setting a title property. The Tooltip component has to be shown or hidden on mouse over and mouse out. In regular Bootstrap (that is, without React), jQuery would be used for injecting these handlers, but in React, we need a cleaner mechanism that fits in the component’s hierarchy.
Although no effort has been spared to ensure that all code listings are accurate, there may be typos or even corrections that did not make it into the book before it went to press. So, do always rely on the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ) as the tested and up-to-date source for all code listings, especially if something does not work as expected.

The Issue List with Bootstrap buttons and a hover tooltip on a Close button
In this section, we’ll style the navigation links in the header and add a footer that is visible on all pages.
For the navigation bar, let’s start with the application title. We’ll move it out of the Issue List page to the navigation bar, where it belongs. The Home, Issue List, and Report links can appear as styled navigation links to the right of the title. Let’s also, on the right side, have an action item for creating a new issue (we’ll move the in-page Add form to a modal in later sections) and an extended dropdown menu for any other actions that may be added in the future. For now, the extended menu will have just one action, called About (which will do nothing for now).
Use an href property on the NavItem: This has the problem that React Router’s <Link> will not be used, and clicking on the href will cause a full browser refresh.
Use React Router’s <Link> instead of NavItem: This will mess up the styling and not align properly, as <Link> uses an anchor tag (<a>) and there’s no way to change that component class.
Either of these options introduces a problem. The recommended way to break this impasse is to use the react-router-bootstrap package, which provides a wrapper called LinkContainer acting as the React Router’s NavLink, at the same time letting its children have their own rendering. We can place a NavItem without an href as a child to the LinkContainer and let the parent LinkContainer deal with the path to the route.

The Issue List page with a navigation bar
The documentation for React-Bootstrap’s Navbar at https://react-bootstrap.github.io/components/navbar/ lists support for a property called fixedTop. It’s meant to keep the NavBar at the top when scrolling vertically. Try this out. You may have to go to the Edit page and/or reduce the screen height to make the vertical scrollbar appear. Do you see a problem? How would you fix it? Hint: Refer to Bootstrap’s Navbar documentation at https://getbootstrap.com/docs/3.3/components/#navbar-fixed-top . (Remember to revert these experimental changes after the exercise.)
Answers are available at the end of the chapter.
Until now, we’ve used horizontal rules (the <hr> element) to demarcate sections within a page. For example, in the Issue List page we used it to separate the filter from the Issue Table. Further, after the inclusion of Bootstrap, you’d have noticed that the left margin has disappeared, causing all the contents of the Issue List page to appear starting right at the window edge on the left side.
Bootstrap’s Panel component is a great way to show sections separately using a border and an optional heading. Let’s use this to decorate the Filter section in the Issue List page. We’ll make changes to the IssueList component by adding a panel around the IssueFilter instance in the render() method.
Even with a panel around the Issue Filter, on testing the application you will find that there is no left margin. The grid system of Bootstrap is the one that adds the margins. Although we won’t use a grid yet, we’ll need to wrap the body of the page with a <Grid> component to add margins. Rather than do this for each page, let’s add the grid component around the Contents component instance in Page.jsx.
There are two kinds of grid containers in Bootstrap: a fluid one, which fills the entire page, and a fixed one (the default), which has a fixed size, but one that adapts to the screen size. Let’s use a fluid grid using the fluid property to match the navigation bar, because it would be nice to let the list of issues fill the screen for better readability.
When you try this set of changes, you’ll find that it’s not obvious that the panel heading is clickable. Firstly, the cursor does not change to something that shows that it’s clickable. Also, the only place you can click is on the text. For the sake of usability, what we’d really like is the cursor to indicate that it’s clickable and let the entire header area be clickable.
If you inspect the DOM using Safari’s or Chrome’s inspectors, you can see that there is an <a> element that is added by React-Bootstrap when the heading is made collapsible. Unfortunately, we don’t have a way of configuring the panel to either not add the <a> (and let you specify a clickable node yourself for the header), or to tell it to fill the horizontal space. The only way we can do this is by using a style that makes the <a> a block element that fills the space and set the cursor.

The Issue Filter within a collapsible panel
Let’s say we’d like the panel to be shown in an expanded state on a browser refresh. How would you achieve this? To make it more interesting, let’s say we want the panel to be expanded if any filter other than the default of all statuses and all effort is in effect. How would you achieve this? Hint: Look up the properties of the Panel component in the React-Bootstrap documentation at https://react-bootstrap.github.io/components/panel/#panels-props-accordion .
Answers are available at the end of the chapter.
Bootstrap can add some niceties to tables apart from styling them. In this section, we’ll convert the plain table into a Bootstrap table, which looks better, expands to fit the screen, and highlights a row on hover. Further, we’ll make the entire row clickable to select the issue so that its description is displayed in the Details section. This will replace the Select link in the Action column. We’ll use the LinkContainer component from react-router-bootstrap to achieve this. We’ll also convert the Edit link into a button, which we couldn’t do along with the other two action buttons because we hadn’t discovered the LinkContainer component then.
striped: Highlights alternate rows with a different background. This could interfere with showing the selected row, so let’s not use this.
bordered: Adds a border around the rows and cells. Let’s use this.
condensed: The default size has too much white space around the text, so let’s use the condensed mode.
hover: Highlights the row under the cursor. Let’s use this.
responsive: On smaller screens, makes the table horizontally scrollable instead of reducing the width of the columns. Let’s use this as well.
At this point in time, the changes will seem to work, but a click on the Close or Delete buttons will also have the side-effect of selecting the row. This is because we now have an onClick() handler on the row (installed by the LinkContainer component), and this is invoked when clicking these buttons. To prevent the event from propagating from the buttons to the contained row, we’ll need to call e.preventDefault() in the handlers. Let’s separate the event handlers into explicit functions (as opposed to anonymous functions within the onClick property ) and use the function names instead.

The Issue List page with the Bootstrap table
Forms can be styled and laid out in a variety of ways using Bootstrap. But before getting into laying out forms, let’s first use the basic components that the library provides to replace the simple <input> and <select> options with the React-Bootstrap equivalents and the labels for these. Let’s do this in the Issue Filter form.
Using React-Bootstrap, the common input types are instantiated using a FormControl . By default, it uses a regular <input> type to render the actual element. The componentClass property can be used to change this default to any other element type, for example, select. The rest of the properties, like value and onChange, are the same for a form control as for the regular <input> or <select> elements.

The Issue Filter using Bootstrap form controls
The default way the form gets laid out in the Issue Filter is not great, as it occupies a lot of space vertically, and the input controls are unnecessarily wide. But this may work well for narrow screens or narrow sections within a page.
A better way to deal with this is to use Bootstrap’s grid system and let each field (and that includes the label) float, that is, occupy the space next to its precedent, or below its precedent if the width of the screen doesn’t allow it. The Issue Filter is a good use case for this behavior, because we’d like to see it laid out horizontally, but on smaller screens, one below the other.
This grid has one row with three cells, occupying four, six, and three columns each. The xs property denotes an “extra small” screen width, therefore, these cell widths are applicable only to a mobile device. The width allocation for other screen sizes can be specified using sm, md, and lg, which stand for small, medium, and large screens, respectively. If not specified, the value applicable to the screen size lesser than this size will be used. Thus, using xs only will mean the same cell widths are used for all screen sizes.
Bootstrap then takes care of laying them out and deciding how to fit the cells at different screen widths. In the filter, we have three cells of roughly equal width: status input (with its label), effort inputs (with their label), and the buttons (together). We don’t want to break either the effort inputs or the buttons into multiple lines even on very small screens, so we’ll treat them as one cell each.
Let’s start with the smallest screen size: a mobile device. Let’s use half the screen width per cell. This will mean that we’ll have Status and Effort on one line, and the buttons on the next. This can be achieved by specifying xs={6}, that is, half the total available 12 columns. You may wonder how three cells of six columns each, totaling 18 columns, can fit a row of 12 columns. But the fact is that the grid system wraps the last six columns into another line (not row, mind you).
It’s best to compare the fluid grid system with paragraphs and lines. Rows are like paragraphs rather than lines. A paragraph (row) can contain multiple lines. As the paragraph width (screen width) reduces, it will need more lines. It’s only when you want to break two sets of sentences (sets of cells) that you really need another paragraph (row). Most people take some time to appreciate this aspect of the fluid grid system, because many popular examples show rows and columns in a fixed grid rather than a fluid one, and therefore lay out the screen in multiple rows.
Next, let’s consider a slightly bigger screen: a tablet, in landscape mode. The property for this size is sm. Let’s fill the screen width with all three cells in one line. We must use a width of four columns for each, thus specifying sm={4} for these cells. If we had more cells, then this too would wrap into multiple lines but since we have exactly three, this will fit the screen in one line.
On larger screens like desktops, we can let each cell continue to occupy four columns each, which doesn’t require any more property specifications. But I think it looks ungainly if the form controls stretch too much, so let’s reduce the width of the cells using md={3} and lg={2}. This will cause the trailing columns on larger screens to be unoccupied.

The Issue Filter with grid system in a very small screen

The Issue Filter with grid system in a small screen
Let’s say the cells are larger and you need the cells to (a) on very small screens, appear one below the other, (b) on small screens, have a max of two cells per line, (c) on medium sized screens, together fit the width, and (d) on large screens, together occupy two-thirds the screen width. What would be the width specifications for the columns in this case?
Although great for mobile devices, the input controls look a bit oversized on a desktop browser. What can be done to make them look smaller? Hint: Look up the React-Bootstrap documentation for forms at https://react-bootstrap.github.io/components/forms/ and look for a property that controls the size. You may find multiple options, so choose the one that you think is best.
Answers are available at the end of the chapter.
Sometimes we want the form controls next to each other, including the labels. This is ideal for small forms with two or three inputs that can all fit in one line and are closely related. This style will suit the Issue Add form. Let’s also replace the placeholders with labels to make them more obvious, which means we’ll have to use FormGroups and ControlLabels as we did for the Filter form.
For the grid-based forms, we didn’t have to enclose the controls or the groups within a <Form>, since the default behavior of the groups was a vertical layout (one below the other, including labels). For inline forms, we need a <Form> with the inline property to wrap around the form controls. This also comes in handy because we need to set the other attributes of the form: name and submit handler.
Unlike the grid-based form, an inline form needs no columns and rows. The FormGroup elements can be placed one after the other. Further, the button does not need a FormGroup around it, and there are no alignment implications if a ControlLabel is not given for a button. As for spacing between the elements, we need to manually add space using {' '} between the label and the control, as well as between form groups.

The Issue Add form as an inline form
Try viewing the new IssueAdd form on a very small screen. What do you see? What does it tell you about Bootstrap and forms?
Let’s say you did not change to use labels for the controls. Do you still need a FormGroup? Try it out, especially in very small screen size.
The widths of the two controls are identical, and it seems we have no control over this. The bsSize property seems to affect only the height. If we want to show a wider Title Input, what can be done?
Answers are available at the end of the chapter.
The next type of form we will explore is the horizontal form, where the label appears to the left of the input, but each field appears one below the other. Typically, the input fills the parent container until the right edge, giving it an aligned look. Let’s change the Issue Edit page to use a horizontal form, since this form has a lot of fields and this kind of form will suit it. While we are at it, let’s also use the validation states that Bootstrap provides to highlight invalid input rather than our own rudimentary method to display validation error in the Date input.
Within the form, we can have the usual FormGroups for each of the editable fields. Within that, we’ll have the control label and the actual control. But that’s not all. We also need to specify how much width the label and the input will occupy. For this, we need to enclose the <ControlLabel> and the <FormControl> within <Col>s and specify the column widths. Since we want it to fill the screen in most cases, we won’t use different widths for different screen sizes, just one specification for the small screen width using the sm property that splits the label and the input in some proportion. The grid system will use the same ratio for bigger screen widths. As for the very small screen width, it will cause it to collapse to a single column. Let’s choose a 3-9 split between the two columns.
Enclosing the <FormControl> with a <Col> works great, but for a <ControlLabel>, this does not have the intended effect of right-aligning the label. The suggested method in the Bootstrap documentation is to set the componentClass of the <Col> to ControlLabel instead. This has the effect of rendering a single element with the combined classes of a ControlLabel and a Col rather than a label within a <div>.
All other controls can be written similarly, with the componentClass for the Status dropdown being select, for the Owner, Title, and Description fields TextInput, for Effort NumInput, and for Due DateInput. All the other properties that the original controls used can be retained as such. For changes to these other controls, refer to Listing 11-17; I am not calling out these changes here for the sake of brevity.
Bootstrap’s form controls support displaying invalid input fields in a distinctive manner. To achieve this, the validationState property can be used in any FormGroup. A value of error for this property makes it display the label and the control in red, as well as an red cross icon to indicate the same within the form control.
At this point, if you test the application, you will find that the date field is not filling the width of the screen. It also looks quite differently styled from the other inputs. The reason is that we are setting the class for the input within DateInput to either 'null' or 'invalid', depending on the validation state. Bootstrap would have normally set a class for the input, and our setting, especially the null, overwrites it.
What we need within the DateInput class is to retain the class that Bootstrap would have set to the <input>. One option is to replace the className with this.props.className. But there could be other properties being passed through, apart from className. So it is safer to use the rest of the properties and pass them through to the <input> element. Also, we don’t need to set the class to invalid, as Bootstrap’s validationState replaces that.

The Issue Edit page as a horizontal Bootstrap form
Add a validation for checking if the title is three characters or more using Bootstrap’s validationState, as we did for the Due field. How is this different in effect from the Due field’s validation? How is it different visually? Why?
Answers are available at the end of the chapter.
Bootstrap provides nicely styled alerts via the Alert component. The first candidate to convert to a Bootstrap-styled alert is the validation message in the Issue Edit page. This could look aligned and styled like the rest of the page. Also it could be subtler. Since the form field itself shows that something is wrong, the error message needn’t be displayed until after the users click Submit. We’ll also let the users dismiss the message after they’ve seen it.

A validation message using Bootstrap Alert in danger style
Let’s now look at result messages and informational alerts, that is, the reporting of successes and failures of an operation. These messages are intended to be unobtrusive, so let’s make them disappear after a few seconds automatically rather than make the users close them. We’ll also let the messages overlay the page as well as transition in and out like the Toast messages in the Android OS.
Since many pages have the need to show such messages, let’s create a new custom component for this that can be reused. Let’s name this new component Toast after the Android OS’s Toast message. We’ll model the interface on the Alert component itself: the visibility will be controlled by the parent, which passes an onDismiss property, which can be called to dismiss it. In addition to the Close icon’s click calling this onDismiss callback, there will also be a timer that calls onDismiss() when it expires. The message it shows can be specified as children to the component.
(These will have to be bound to this, the code for which I am not showing explicitly here. Refer to Listing 11-23.)
The actual changes to each of the components is shown in the following listings, with minor variations in each component. In the IssueList component, let’s add a success message on successful deletion of an issue in addition to the previous changes. In the IssueDetail component, there is no need for a success message, so that method and its binding are not included.

The Toast message in the Edit page
In this section, we’ll replace the in-page IssueAdd component with a modal dialog that is launched by clicking the Create Issue navigation item in the header. This is so that the user can create an issue from anywhere in the application, not just from the Issue List page. Further, when the new issue is submitted, we’ll show the newly created issue in the Issue Edit page, because this can be done regardless of where the dialog was launched from.
Instead of a modal dialog, the Create Issue can also be a separate page. But a modal works better when the number of required fields is small; the user can quickly create the issue and later fill up more information if required.
When a modal is rendered, it is rendered outside the main <div> of the DOM that holds the page. Thus, in terms of code placement, it can be placed anywhere in the component hierarchy. In order to launch or dismiss the modal, the Create Issue navigation item is the controlling component. So, let’s create a component that is self-contained: it displays the navigation item, launches the dialog and controls its visibility, creates the issue, and routes to the Issue Edit page on a successful creation. Let’s call this new component IssueAddNavItem and place it in a file called IssueAddNavItem.jsx.

The Create Issue modal dialog
Adding styles and themes to an application in a MERN stack is no different from any other stack because the important part is the CSS and how styles are handled by various browsers. And they don’t vary depending on the chosen stack. Bootstrap, a pioneer in this area, enabled browser independence and a responsive behavior out of the box. React-Bootstrap replaced the separate JavaScript code that dealt with the Bootstrap elements and made self-contained components possible.
We could have used Material-UI or any other framework to achieve what was required, but the take-away from this chapter should be a peek into how you can design your own styled reusable UI components if and when required. It would also be good if you took a look at the documentation of React-Bootstrap at https://react-bootstrap.github.io/getting-started/introduction and Bootstrap itself at http://getbootstrap.com/docs/3.3/ to get a grasp of the kinds of components that these two libraries offer.
At this point in time, the application may look complete except for some advanced features. But if the application needed search engine bots to be able to index the pages naturally, it will need the ability to serve fully constructed pages directly from the server, as they would appear in the browser. This is because search engines usually do not run the JavaScript in the pages that they crawl, and for an SPA, this is essential to construct the pages in the browser.
In the next chapter, you’ll learn how to do construct the HTML on the server and respond to the client and, more importantly, how to do this using the same codebase on the client as well as the UI server.
Just using the fixedTop property will cause the navigation bar to overlay the top portion of the content. To fix it, you’ll need to add a padding to the body tag as suggested in the Bootstrap documentation.
Often, you will need to refer to the Bootstrap documentation in addition to the React-Bootstrap documentation because React-Bootstrap is based on Bootstrap. When you do that, remember to choose the Version 3.3 documentation rather than the latest version 4 documentation of Bootstrap.
If the cells were larger, a specification of xs={12} sm={6} md={4} lg={3} would work best. On very small and small screens, you’ll see multiple lines, and on medium and large screens, the cells will fit into a single line.
To make the controls look smaller, you can use the bsSize="small" property on the FormGroups. It could be done on the FormControls too, but using it on the FormGroup makes it affect the labels too. But this does not work for buttons—we must specify the property for each button instead.
On a very small screen, the controls look like a default form, the labels as well as the controls one below the other. The React-Bootstrap Form component has media-queries that make this transition, just like the columns in the grid system.
No, a form group is not really required to show the form controls inline, next to one another. But without a form group, on a very small screen, the controls are too close together and not visually appealing. Thus, even when not using labels, it is best to surround the controls with a FormGroup.
To specify an exact width, you must use an inline style, like style={{ width: 300 }}. Without a width specification, the control fills the width of the screen on very small screens. With a width, it takes the width specified on all screen sizes. In effect, if we do set the width, it’s better to set the size on all controls rather than some.
Although this does show the error (using red borders around the control and the label in red font), a submit is not prevented. This is because we have not let TextInput handle length errors and notify the IssueEdit component so that it can update invalidFields.
Visually, you’ll find that the red X is absent in case of invalid input in the Title field. The reason is the same as that for the DateInput before we made changes to this section: we have not passed through props such as class that Bootstrap could be adding to the component.
In this chapter, we’ll explore another cornerstone of React, the ability to generate HTML on the server in addition to being able to render directly to the DOM. This enables creation of isomorphic applications, that is, applications that use the same codebase on the server as well as the client to do either task: render to the DOM or create HTML.
Server rendering (also known as server-side rendering or SSR for short) is the opposite of what characterizes an SPA: rather than fetch data via APIs and construct the DOM on the browser, the entire HTML is constructed on the server and sent to the browser. The need for it arises when the application needs to be indexed by search engines. Search engine bots typically start from the root URL (/) and then traverse all the hyperlinks present in the resulting HTML. They do not execute JavaScript to fetch data via Ajax calls and look at the changed DOM. So, to have pages from an application be properly indexed by search engines, the server needs to respond with the same HTML that will result after the Ajax call in componentDidMount() methods and subsequent re-rendering of the page.
The first time any page is opened in the application (e.g., by typing in the URL in the browser, or even choosing refresh on the browser when already in any page in the application), the entire page will be constructed and returned from the server. We’ll call this server rendering.
Once any page is loaded and the user navigates to another page, we’ll make it work like an SPA. That is, only the API will be made and the DOM will be modified directly on the browser. We’ll call this browser rendering.
Note that this is true for any page, not just for the home page. For example, the user could type in the URL of the Edit page of an issue in the browser, and that page will be constructed at the server and returned too.
Since the steps to achieve all this are not simple, we’ll create a new simple page—the About page—for mastering the techniques required. We’ll start with a static page, then add complexity by using an API to fetch the data it renders. Once we’ve perfected the technique to render the About page at the server, we’ll extend the changes required to all other pages.
A note of caution: not all applications need server rendering. If an application does not need to be indexed by search engines, the complexity introduced by server rendering can be avoided. Typically, if the pages do not have public access, they don’t need search engine indexing. The performance benefits alone do not justify the complexity of server rendering.
All the shared files, essentially, all the React components.
A set of files used to run the UI server using Express. This will import the shared React components for server rendering.
A starting point for the browser bundle, one that includes all the shared React components and can be sent to the browser to execute.
After these changes, the application should work just as before. You should test both with HMR enabled as well as HMR disabled and manually compile the browser bundle using npm run compile before starting the server.
We used the ReactDOM.render() method to render a React element into the DOM. The counterpart method that is to be used to create an HTML on the server side is ReactDOMServer.renderToString(). Although the method itself is a simple one, the changes that we need to make in order to use it are not. So, let’s use a simple About component to get familiar with the fundamentals. Then, in later sections, we’ll use the same technique for the other components in the application.

The About page

Sequence diagram for browser rendering
The user types in the URL of the home page or refreshes the browser at the home page.
The UI server returns index.html, which has a reference to the JavaScript app.bundle.js. That is also fetched, and it contains the react components, including the About component. Now, the page is considered loaded. (The Issue List component will also be mounted, but that’s not of importance at the moment.)
The user clicks on the link to the About page.
React mounts and renders the About component, whose code is part of the JavaScript bundle. At this point, all the static text in the component is seen.
Once the initial mount is completed, componentDidMount() is called, which will trigger a call to the API server to fetch the data for the component. We have not implemented this yet, but you should be able to appreciate this by considering the other pages that we have implemented, for example, the Issue List page.
On successful fetch of the data using the API call, the component is re-rendered with the data.
But this is going to produce only the markup for the About component. We still need the rest of the HTML such as the <head> section, with the component inserted in the contents <div>. So, let’s make a template out of the existing index.html that can accept the contents of the <div> and return the complete HTML.
We’ll eventually add the scripts, but for the moment, it’s better to test the changes without this complication. As for import of the About component and rendering it to string, let’s do this in a new file in the server directory, in a function called render() . The function will take in a regular request and response like any Express route handler. It will then send out the template as a response, with the body replaced by the markup created from ReactDOMServer.renderToString().

The server rendered About page

Sequence diagram for server rendering
The user types in the URL for the About page (or chooses refresh in the browser while on the About page).
The browser sends a request to /about to the UI server.
The UI server fetches the data required for the page from the API server using a GraphQL API call. We’ll implement this in later sections in this chapter.
On the UI server, ReactDOM.renderToString() is called with the About component and its data.
The server returns an HTML, with the markup for the About page included in it.
The browser converts the HTML to the DOM and the About page is visible in the browser.
Let’s say the string representation of the component rendered in the server was quite large. Creating a string from the template in memory would take up a lot of memory and be slow. What option do you have in this case? Hint: Look up the documentation of ReactDOMServer at https://reactjs.org/docs/react-dom-server.html to see what other methods are available.
Answers are available at the end of the chapter.
At this point, we just have a simple About component. We’ll need it to get data by calling the about API and rendering it on the server. We’ll do all that in the following sections, but before that, let’s deal with the inconvenience of having to compile About.js manually from About.jsx. Soon, we’ll have to compile all the files under the src directory for inclusion in the server side, and a manual compilation can become unviable.
Further, you also saw that the import/export paradigm and require/module.exports paradigm, although compatible, are not convenient when mixed. One needs to remember adding the .default after every require() of a file that uses the import/export paradigm.
It turns out that Webpack can be used for the server as well, and it can compile JSX on the fly. This will also let us consistently use the import/export paradigm in the UI server codebase. Webpack works quite the same as with the front-end code, but for one difference. Many server-side Node packages such as Express are not compatible with Webpack. They import other packages dynamically, making it hard for Webpack to follow the dependency chain. So, we’ll have to exclude the third-party libraries from the bundle and rely on node_modules to be present in the UI server’s file system.
Since we are now executing a bundle, when any error is encountered on the server, the line numbers that are shown in stack traces are that of the bundle’s. This is not at all convenient when we encounter errors. The source-map-support module solves this problem. On the front-end, the source-map support module also made it convenient to add breakpoints. On the back-end, all it does is make error messages more readable.
Now, you can start the application using npm start and check it out. There should be no changes in the application’s behavior. You can try the About page, both directly and by loading /issues and navigating from there. The two avatars will continue to be different since we are yet to return an HTML with the navigation bar, etc. while rendering from the server.
Although using Webpack for the server does simplify the compilation process, you’ll find that during development, you still need to restart the server for every change. You could use the nodemon wrapper by running npm start, but even there you’ll find that the front-end HMR doesn’t work. This is because, on a restart, the HMR middleware is reinstalled, but the browser tries to connect to the original HMR, which is no longer there.
The solution to all this is to automatically reload the modules even in the back-end, using HMR. Since we are using Webpack to bundle the server, this should work. But the fact is that the Express already has stored references to any existing modules and it needs to be told to replace these modules when accepting HMR changes. Although it can be done, setting this up is quite complex.
So, let’s take the easy way out: we’ll only reload changes to modules based on the shared folder. As for changes to uiserver.js itself, we expect these to be very few and far between, so let’s restart the server manually when this file is changed and use HMR for the rest of the code that it includes.
Finally, let’s change package.json’s script section to add convenience scripts for starting the UI server. We can now change the start script to remove nodemon (since HMR will load the modules automatically). Then, let’s replace the watch script with a watch-server-hmr script that runs the webpack.serverHMR.js configuration in the watch mode. Since both this and the start script are needed for starting the UI server in development mode, let’s add a script called dev-all that does both, one after the other.
In npm scripts, multiple commands can be combined using the & operator. The commands are started up simultaneously. Just to safeguard the server.js bundle being built before the npm start command is run, it’s good to have a sleep command before the npm start command. The amount of time to wait can vary depending on how fast your computer is and how long it takes to compile the server files. To start off, you could use a sleep timer of five seconds and customize this based on your needs.
On a Windows PC, you may need to create your own batch file with equivalent commands or execute npm watch-server-hmr and npm start on different command windows.
Now, you can stop all other UI server commands and restart it using the single npm run dev-all command. The application should work just as before, but most changes should automatically reflect without having to restart this command.
The way the About page was rendered from the server was different from the way it was rendered by navigating to it from /issues. In the first case, it was displayed without the navigation bar, and in the second, with it.
The reason this happened is as follows. On the browser, App.jsx mounted the Page component on to the contents div. But, on the server, the About component was rendered directly within the contents div, by stuffing it in the template.
On the server, wrapping a Router around the page, or using Switch or NavLinks, will throw up errors. This is because the Router is really meant for the DOM, where on clicking of a route’s link, the browser’s history is manipulated and different components are loaded based on the routing rules.
On the server, React Router recommends that we use a StaticRouter in place of a BrowserRouter. Also, whereas the BrowserRouter looks at the browser’s URL, the StaticRouter has to be supplied the URL. Based on this, the router will choose an appropriate component to render. StaticRouter takes a property called location, which is a static URL that the rest of the rendering will need. It also needs a property called context, whose purpose is not obvious right now, so let’s just supply an empty object for it.
Now, if you test the application, you will find that both server rendering and browser rendering are identical for the About page: the navigation bar will appear in both cases. To test server rendering, you will need to press refresh on the browser while on the About page. As for browser rendering, you will need to refresh the browser in another page, say the Issue List page, and then navigate to the About page using the extended menu. Refer to the screenshot in Figure 12-1 to recap how it looks.
Note that at this point, except for the About page, the other pages are only being rendered on the browser, even when refreshing. We will address that soon, once we perfect the About page server rendering.
Press refresh on the browser while on the About page, to display the page using server rendering. Try to create a new issue by clicking on the Create Issue menu item (+ icon) in the navigation bar. What happens? Can you explain this? Hint: (a) Try to put a breakpoint in showModal() method in IssueAddNavItem and then (b) inspect the + icon using the browser’s Developer Tools. Check out the event listeners attached to it. Try these after clicking on the Home menu and note the difference.
Press refresh on the browser while on the About page, to display the page using server rendering. Use the Developer Tools to inspect the network calls and then navigate to any other page, say the Issue List page. Do the same by starting at the Report page rather than the About page. What differences do you see, and why?
Answers are available at the end of the chapter.
Although the page looks as it is supposed to now, there is still a problem with it. If you tried the exercise at the end of the previous section, you will realize that what was rendered was pure HTML markup, without any JavaScript code or event handlers. Thus, there is no user interaction possible in the page.
Now, if you test the application by refreshing the About page, you’ll find that the + button works! This means event handlers have been attached. But you’ll also see a warning on the console to this effect:
Warning: render(): Calling ReactDOM.render() to hydrate server-rendered markup will stop working in React v17. Replace the ReactDOM.render() call with ReactDOM.hydrate() if you want React to attach to the server HTML.
When testing with this change, you will find that the warning has disappeared and that all event handlers have been installed. You can see the effect not only when you click on the + button, but also when navigating to other tabs in the navigation bar. Earlier, these would have caused browser refreshes whereas now these navigations will load the appropriate component into the DOM directly, with React Router doing its magic.

Server rendering sequence diagram updated with Hydrate
Rather than the plain About component, the server returns script tags for the application and React and other library source code bundles.
The About page is viewable, but not interactive. There is no real change in the execution here, only that the diagram explicitly states that the page is not interactive.
The browser fetches the JavaScript bundles and executes them. As part of this, ReactDOM.hydrate() is executed with the routed page as the root element.
ReactDOM. hydrate() causes event handlers to be attached to all components, and now the page is viewable and interactive.
We used a hard-coded message in the About component to get off the ground. In reality, this string should come from the API server. Specifically, the about API’s result should be displayed in place of the hard-coded version string for the API.
If we were to follow the same pattern as the other components that loaded up data from the API, we would have implemented the data fetching in the lifecycle method componentDidMount() and set the state of the component. But in this case, we really need the API’s return value to be available when the component is being rendered on the server.
But then, how do we pass this information down to the About component while it’s being rendered? One way to do this is by passing it to the Page component as props, which in turn can pass it along to the Contents component, and then finally to About. But this is a hindrance and causes excess coupling between components—neither Page nor Contents need to know about the data that is of relevance only to About.
If you test this, you’ll be surprised to find that the About page shows the API version as “unknown” instead of the value fetched from the API. Do take a look at the page’s source (use Developer Tools to inspect the page source), and you will find that the HTML indeed has the API version string from the server. Then, why does it not show up in the screen?
If you look at the Developer Console, you’ll see an error message like this:
Warning: Text content did not match. Server: "Issue Tracker API v1.0" Client: "unknown"
That should give you a hint as to the underlying problem. We’ll address this issue in the next section.
React expects that the rendered content is identical between the server and the client. It can patch up differences in text content, but you should treat mismatches as bugs and fix them. In development mode, React warns about mismatches during hydration. There are no guarantees that attribute differences “patched up in case of mismatches. This is important for performance reasons because in most apps, mismatches are rare, and so validating all markup would be prohibitively expensive.
If you think about it, it makes a lot of sense. When hydrating (or attaching event handlers to the DOM), things can get ambiguous if the tree generated by the hydrate() call doesn’t match the tree that is already there, rendered by the server. Note that hydrate() is just a variation of render()—it really creates a virtual DOM with event handlers that can be synced to the actual DOM.
What is needed is to make the browser render identical to the server render. But this requires that the same data be used to render the component on the server as well as the browser. An API call during the browser render (for example, in the lifecycle method componentDidMount()) will not cut it because it is asynchronous. We need the data when the component is being rendered for the first time.
The recommended way to do this is to pass the same initial data resulting from the API call to the browser in the form of a script and use that to initialize the global store. This way, when the component is being rendered, it will have the same data as the component that was rendered at the server.
Now, when the component is rendered in the browser, it will have the same initial data in the store as it did when it was rendered on the server. If you test the application by refreshing the browser while in the About page, you will find that the React error message is no longer shown. This indicates that the result of the browser rendering matched the server rendering, allowing React to attach event handlers without any mismatches.
The other pages will still show an error, for example, the /issues URL will throw the following error in the console:
Warning: Expected server HTML to contain a matching <div> in <div>.
The original index.html is being returned for the URL /issues, which just has an empty <div> in the body, because it was not server-rendered like in the case of the About page. When React renders the DOM in the browser during the call to hydrate(), the actual page is rendered. Thus, there is a mismatch in what the server rendered and what the browser rendered, and therefore the error. We’ll address this in later sections in this chapter, when we synchronize the server and browser data for all pages in a generic manner.
At this point, although refreshing the browser with the URL pointing at /about works well, you’ll find that navigating to the About page after starting from any other page, say /issues, does not show the API version from the API server. That’s because we never added a data fetcher in the About component that could be used to populate its message to take care of the case where it is mounted only on the browser.
After this change, you can test the application, in particular, load the home page or /issues and then navigate to the About page. You should see the correct API version being shown. You can also confirm that a call to the API is being made by inspecting the Network tab of the Developer Tools.
In this section, we’ll fix the mismatch errors that React is showing for the rest of the pages. We’ll also lay down the framework that deals with fetching data in a generic manner, so that we can remove the call to About.fetchData() in render.jsx, and make it fetch data that is appropriate for the component that will actually be rendered within the page.
This change will need a restart of the server since HMR does not handle changes to uiserver.js itself. On testing the application, you will find that the React error for mismatched <div> for all the pages is no longer seen. If you inspect the page source, you will find that the server returns a full page with the navigation bar, etc., but without the data.
For example, when you refresh the page /issues, you will see that the table header is present, but the table itself is not populated with issues. It matches the browser rendering because even in the browser, the initial render starts with an empty set of issues. Only during componentDidMount() is the list of issues fetched from the API and populated in the table. We’ll address this in the following sections. For now, let’s ensure that we have the ability to determine what data needs to be fetched based on the matching route.
The main problem we need to solve is that the data required via API calls needs to be available before rendering is initiated on the server. The only way this can be done is by keeping a common source of truth for the list of routes available. Then, we could match the request’s URL against each route and figure out which component (and therefore, which fetchData() method) will match. The same source of truth should also be responsible for generating the actual <Route> components during a render.
While rendering, both on the server as well as the browser, one of the routes will be chosen for rendering based on the URL. On the browser, the history object of BrowserRouter will supply the URL for matching, and on the server, we’ve supplied it via the location property of the StaticRouter, which we used to wrap the page.
With these changes, we have managed to get rid of the hard-coding of the data that needs to be fetched based on the component that will be rendered for the matched route. But we’ve not yet implemented the fetching of data in many of the components. For example, refreshing the browser on /issues will continue to render an empty table that’s later filled with the list of issues fetched from an API call in the browser. This is not what is needed: a request to /issues should result in a page replete with the list of issues that match the filter. But there are nuances that are different from what we did for the About component: these API calls vary depending on parameters. As we set out to implement the data fetcher in each of the existing components, we’ll explore how these parameters can be passed through for use in rendering.
In this section, we’ll make the IssueEdit component render from the server with the data that it requires prepopulated.
To start, let’s separate the data fetcher into a static method as we had done in the About component. This method relies on the ID of the issue to fetch the data. The most generic entity that has this information is the match object that the component has access to automatically while rendering on the browser.
This is because we wrote out the contents of the issue object using JSON.stringify(), which converts dates to strings. When we made an API call via graphQLFetch, we used a JSON date reviver function to convert strings to date objects, but the script that initializes the data does not use JSON.parse() . The script is executed as is. You can take a look at the source file using View Page Source in your browser, and you’ll find that the key created is set to a string.
If you test the application after this change, you will find that the error message is gone, and the Issue Edit page shows the issue properly, including the date fields. Further, if you navigate to the About page, you should see that it loads the API version from the API server (the Network tab of the Developer Tools will show this). This proves that the initial data for one page does not affect the rendering of another page.
Is there a way to use the JSON date reviver strategy for the initial data also? How about the reverse: Is there a way to use serialize() instead of JSON.parse() for the API calls? Should we do it?
Answers are available at the end of the chapter
In this section, we’ll implement the data fetcher in the IssueList component. In the IssueEdit component, we dealt with the fact that the data fetcher needed the parameters of the matching route. In IssueList, we’ll deal with the fact that the search (query) string part of the URL is needed for fetching the correct set of issues.
If you test the application, especially the Issue List page now, you should find that a refresh on the Issue List shows the issues filled in the table. This can be confirmed by inspecting the source of the page: you should find that the table is prefilled. Also, if you watch the Network tab of the Developer Tools, you should not see any API call being made to fetch the list of issues on a refresh, whereas the API call will be made when navigating from any other page. Do also test with different values of filters to ensure that the search string is being used correctly.
We still have one more component to deal with: IssueDetail. At this juncture, the component will seem to work, both when clicking on an issue row in the list and when the browser is refreshed with the URL containing an issue ID such as /issues/1. But you’ll find that the detail is being fetched after mounting the component and not as part of the server rendered HTML. As discussed, this is not good. We really need the detail part also to be rendered in the server.
Although React Router’s dynamic routing works great when navigating via links in the UI, it is quite inconvenient when it comes to server rendering. We cannot easily deal with nested routes. One option is to add nesting of routes in routes.js and pass the nested route object to the containing component so that it can create a <Route> component at the appropriate place based on this.
The route specification remains simple and has only the top-level pages in a flat structure, without any hierarchy.
It gives us an opportunity to combine two API calls into one in the case where the Issue List is loaded with a selected issue.
If you test the Issue List page now, especially with any one issue selected and refreshing the browser, you’ll find the detail of the selected issue loaded along with the list of issues. If you change the selected issue by clicking on another row, you will see in the Network tab of the Developer Tools that a single GraphQL call is fetching both the list of issues as well as the details of the selected issue.
When changing the selected issue, although it’s a single GraphQL call, the entire issue list is being fetched. This is not required and adds to the network traffic. How would you optimize for this?
Answers are available at the end of the chapter
We still have one last thing to take care of: a request to the home page, that is, /, returns an HTML from the server that contains an empty page. It seems to work because after rendering on the browser, React Router does a redirect to /issues in the browser history. What we really need is for the server itself to respond with a 301 Redirect so that the browser fetches /issues instead from the server. This way, search engine bots also will get the same contents for a request to / as they get for /issues.
React Router’s StaticRouter handles this by setting a variable called url in any context that is passed to it. We’ve been passing an empty, unnamed context to StaticRouter. Instead, let’s pass a named object, though empty, to it. After rendering to string, if the url property is set in this object, it means that the router matched a redirect to this URL. All we need to do is send a redirect in the response instead of the templated response.
Now, if you enter http://localhost:8000/ in your browser, you should see that the Issue List page loads without any flicker. In the Network tab, you will find the very first request resulting in a redirect to /issues, which then follows the regular path of rendering the issue list at the server.
This chapter may have been a little heavy since we used complex constructs and patterns to implement server rendering. Hopefully, using the About page eased the complexity and helped you understand the fundamental concepts for server rendering.
It must also be evident by now that React itself, not being a framework, does not dictate each of the additional parts that complete the application. React Router helped us a bit with front-end routing, but some of it did not work for server rendering. We had to invent our own patterns of generated routes and that of data fetchers as static methods in each of the routed components to deal with data associated with server rendered pages.
As we move on to the next chapter, we won’t focus on one single feature or concept. Instead we’ll implement features that are common to many applications. As we do this, we’ll see how the MERN stack satisfies the needs of these advanced features.
The ReactDOMServer method renderToNodeStream() can be considered to replace renderToString(). This method returns a stream that can be piped to the Express response stream. Instead of a template, we’ll need pre- and a post-body strings that we can write to the response before and after piping the node stream, respectively.
When the About page is rendered using server rendering, you will find that clicking on the + menu item does nothing. There are no event handlers attached to the menu item, further, you will find that there is no code where you can put a breakpoint. The reason is that the template does not include the JavaScript bundles that contained both the component as well as React library code.
In a browser-rendered navigation bar, clicking on a link does not load the page from the server. Only XHR calls are made to get the data and the DOM is constructed on the browser. In a server-rendered navigation bar, clicking on a link loads the page from the server, just like a normal href would have done. The reason is the same as in the previous exercise: in a server-rendered page, there are no event handlers attached that trap the click event and make changes on the DOM within the browser. In a server-rendered page, the links behave as pure href links do: they make the browser load a new page.
You could use JSON.parse(), but it needs a string as its argument. Since the string representation of the initial data itself has a lot of double quotes, they need to be escaped, or you could use single quotes around it. Another strategy used by some is to use a hidden textarea or a div to store the string and read it off the DOM and call JSON.parse() on that string. I find that serialize is a much more concise and clearer option.
As for the reverse, the caller of the API will need to use an eval() instead of JSON.parse() on the resulting data. This is quite dangerous because it would allow new functions to be installed as a result, if the data contained any. If the API server has somehow been compromised, this can enable malicious code to be injected into the browser. Further, this strategy assumes that the caller works on JavaScript, and this may not be a valid assumption.
A good strategy to optimize the data being fetched is to write another method that fetches the selected issue alone via a different GraphQL query. This method, say loadSelectedIssue(), can be called from componentDidUpdate() if the search did not change but the ID parameter changed.
In this chapter, we’ll take a look at features common to many applications. These features cut across the different technologies (front-end, back-end, database) that the MERN stack consists of, and requires us to put together changes in all of them to make them work.
We’ll first refactor the UI code to reuse common code across many components that display the Toast messages. We’ll move most of the repeated code in the components into new file using a pattern common to React. Then, we’ll implement the Report page that has until now been a placeholder. This will require us to use the aggregate function of MongoDB. Then we’ll implement pagination in the Issue List page to deal with large lists. This will exercise another feature of MongoDB: the skip and offset options of find().
We’ll then implement an Undo operation when deleting issues to resurrect them. Finally, we’ll display a search bar in which the users can type keywords and look for issues matching the keywords.
The state variables that are used for the Toast: showing state, the message, and the message type
The methods showError(), showSuccess(), and dismissToast()
The placement of the Toast component within the render() function
With these changes, the application should continue to behave as before. You can test it by testing each of the error or success messages that these components show.
Although no effort has been spared to ensure that all code listings are accurate, there may be typos or even corrections that did not make it into the book before it went to press. So, do always rely on the GitHub repository ( https://github.com/vasansr/pro-mern-stack-2 ) as the tested and up-to-date source for all code listings, especially if something does not work as expected.
We had left a placeholder for reports in the navigation bar until now. In preparation for implementing this page in the next two sections, let’s explore what MongoDB provides in terms of getting summary data of a collection, that is, aggregates.
MongoDB provides the collection method aggregate() to summarize and perform various other read tasks on the collection using a pipeline. A pipeline is a series of transforms on the collection before returning the result set. In fact, the default call to aggregate() without any arguments is identical to a call to find(), that is, it returns the entire list of documents in the collection, without any manipulation.
The MongoDB aggregation pipeline consists of stages. Each stage transforms the documents as they pass through the pipeline. For example, a match stage will act like a filter on the list of documents from the previous stage. To simulate a find() with a filter, a single match stage in the pipeline can be used. To transform the document, a project stage can be used. This, unlike the projection in find(), can even add new calculated fields to the document using expressions.
Each stage does not have to produce a one-to-one mapping of the previous stage. The group stage is one such stage that produces a summary rather than replicate each document. The unwind stage is something that does the opposite: it expands array fields into one document for each array element. The same stage can appear multiple times—for example, you could start with a match, then a group, and then another match to filter out some documents after the grouping.
For a full listing of all available stages, refer to the MongoDB documentation on the pipeline stages at https://docs.mongodb.com/manual/reference/operator/aggregation/ . I’ll only discuss in depth the two stages match (based on the filter) and group (to summarize the counts) that are needed to implement the Report page in the Issue Tracker application.
The group stage is a little more involved. It consists of a set of fields that need to be created, and a specification of how they need to be created. In the object specification, the key is the name of the field, and the value specifies how the field’s value is to be constructed. The values are typically based on existing fields in the document (in fact the results of the previous stage), and to refer to these fields, one needs to use a $ prefix, without which it will be taken as a literal.
The _id field is mandatory and has a special meaning: it is the value that the results are grouped by. Typically, you use an existing field specification for this, the grouping will be done on this field’s value. For the rest of the fields in the output, you can specify an aggregation function to construct their values.
For the Report page, let’s create a pivot table (or a cross-tab) output that shows the counts of issues assigned to various owners, further grouped by statuses. For this, we need two grouped by fields, the owner and the status. Multiple group fields can be specified by setting the _id field to an object instead of a string. This object needs to contain the name of the output and the field identifier for each of the fields. Then, in the output, rather than a string as the value of _id, an object will be returned, each returned row with a different combination of the two fields.
That’s the final structure of the query that we will use in the next section to implement the API that will help build the Report page.
Rather than converting the stats object to an array, could we have returned the stats object as is? How would the schema look? Hint: Look up the Apollo GraphQL issue at https://github.com/apollographql/apollo/issues/5 .
Answers are available at the end of the chapter.
Now that we have a working API, let’s construct the UI for the Report page. We’ll use a format that is popularly known as a cross-tab or a pivot table: a table with one axis labeled with the statuses and the other axis with owners.
Since the number of owners can be many and there are a limited number of statuses, let’s line up the statuses on the horizontal axis (table row header) and use one row per owner to show the count of issues assigned to that owner. This way, we could handle a large number of owners easily. Let’s replace the stateless component placeholder in IssueReport with a regular component inherited from React.component and start with the render() method. We’ll use a collapsible panel and place the IssueFilter component here, just like we did for the Issue List. Following this, let’s show a table with the report, one row for every value in the data returned by the API.
The syntax <> is a JSX shortcut for <React.Fragment>.
A constructor to fetch the initial data from the store and delete it after consumption
A componentDidMount() method to load the data in case it has not been loaded yet
A componentDidUpdate() method to check if the search string has changed, and if so, reload the data
A loadData() method that these two lifecycle methods can call to load it and set the state
A Toast wrapper, which will need to be exported rather than the original class

The Report page
Let’s say you needed row totals. How would you go about implementing this? Try it out.
How about column totals? How can these be implemented?
Answers are available at the end of the chapter.
You perhaps by now have noticed that the Issue List page has become unwieldy because it displays all the issues in the database. In this and the next section, we’ll implement pagination so that the user is shown a limited set of issues and can navigate to other pages. Let’s keep the UI for the next section and modify the List API to support pagination in this section.
In order to show a pagination bar, we’ll also need the total count of pages for the list. So, let’s first modify the schema to add a count of pages in addition to the list of issues. Instead of returning the list of issues directly, we’ll need to return an object that contains the list as well as a page count. Then, in addition to the filter specification, we need to specify which page to fetch.
It’s not a good practice to change a GraphQL API in a real-life project, because it would break the UI application. The recommended practice is to create a new API, for example, a query called issueListWithPages for this purpose, especially if the application is already in production. But I’m modifying the existing API so that the final code is concise.
Note that the count() method is asynchronous, but this ends up evaluating the cursor’s contents. So, the next call on the cursor toArray() can be synchronously invoked. The argument to the count() function takes in a Boolean that determines whether the count to be returned takes into account the effect of skip() and limit() or not. Using the argument’s value as false gives us the total count of objects that would have matched the filter. This is the count that we need.
Since the return value in the schema has changed, we’ll also need to change the caller, the IssueList component to accommodate the change. Instead of using the value issueList directly from the data, we’ll need to use it as issueList.issues. We’ll not implement pagination just yet; this change is only to ensure that the Issue List page continues to work, by showing the first 10 issues.
Further, you can test the Issue List page to ensure that the API changes haven’t broken anything, except that you should now be seeing only the first page (that is, 10 issues) rather than all the 100 or so issues.
Let’s now use the new API that we coded in the previous section to display a bar of pages.
The npm page says that it is not actively maintained.
The resulting page items are buttons and not links, making it hard for search engine bots to index them. What we should prefer is a <Link> or equivalent that works well with React Router.
So, let’s create our own minimalistic pagination bar that shows pages in chunks of five. To go to the next or previous section, let’s use the > and < indicators at the each end of the bar. The math required for this is simple and good enough to demonstrate what can be done.

Issue List screen with the pagination bar on page 8
A far more intuitive and creative pagination can be created based on the current page, which also lets the users go to the end or the beginning of the pages. But this should have given you the basic building blocks of how to go about implementing pagination in the MERN stack.
The approach of using the same cursor to fetch the count was okay for small data sets, but it can’t be used for larger data sets. The problem with a pagination that knows the number of pages is that it needs the total count of documents in the filtered set.
The fact is that in any database, counting the number of matches is expensive. The only way it can be done is by applying the filter and visiting every document to check whether it matches the filter. Unless, of course, you have indexes for every possible filter combination, which either means limiting the kind of filters you want to allow the user, or spending enormous storage capacity for indexing all combinations.
I find that, practically, it isn’t of much use to show the exact number of pages or count of matched records, when the result is possibly very large. If it’s indeed hundreds of pages long, it is highly unlikely that the user will want to go to exactly the 97th page, or even the last page. In such cases, it’s advisable to just show the Previous and Next links and not query the total count in every request. React-Bootstrap’s Pager component will work well for this approach.
If a user (or a search engine bot) is unlikely to go beyond the few initial pages, this approach will surely work. But it so happens that even a skip() operation has to traverse all the documents that are being skipped to get to the start of the page being displayed. If there were a million documents, for example, and the user (or a search engine bot) were to traverse all the way to the last page, it would mean that the last page would retrieve all the million documents before returning the list corresponding to the last page.
The ideal strategy to use with these large sets is to return a value in the API’s return that indicates where the next page starts, in terms of an indexed field value. In the case of the Issue Tracker application, the ID field is ideal for this. With this strategy, you wouldn’t use the skip() operation; instead, you use the ID as a filter to start from, using the $gte operator. Since the ID field is indexed, the database wouldn’t have to skip so many documents to arrive at this ID; it would directly reach the document and traverse from thereon to fetch one page of documents. The disabling of previous and next buttons becomes non-trivial in these cases and is beyond the scope of this book.
The currently active page had to be calculated from the search string, once in fetchData() method and again in the render() method. In this case, it was perhaps a simple operation, but in cases where this may require lots of code and/or be computationally expensive, should you consider saving this value in the state? What are the pros and cons? Hint: Look up the “Thinking in React” page at https://reactjs.org/docs/thinking-in-react.html#step-3-identify-the-minimal-but-complete-representation-of-ui-state .
Answers are available at the end of the chapter.
The next feature that we’ll implement is an undo action on the delete operation. The older convention for destructive operations such as a delete was to ask for confirmation from the user. But usability is enhanced if we don’t ask for confirmation, because it is quite rare that the user answers “No” to an “Are you sure?” question. If the user mistakenly deletes the issue, providing an undo feature works far better.
Next, the actual implementation of the API is somewhat similar to the Delete API itself. The difference is that, instead of moving from the issues collection to the deleted_issues collection, the restore API has to transfer an issue in the reverse direction: from the deleted_issues collection to the issues collection. Let’s copy the code from the remove() function and swap these two collection names.
It should return a success value, and if you refresh the browser, you should see that the deleted issue has been restored.
The best place to initiate an undo of a delete operation is in the Toast message that shows that the issue has been deleted. Within the Toast message indicating the success of the delete operation, let’s include a button that can be clicked to initiate the undo. This needs to be done in the IssueList component.

The Delete success Toast with an Undo link
A search bar in most applications lets you find documents by just typing in some words. We’ll implement this not like a search filter, but as an autocomplete that finds all issues matching the words typed, and lets the user pick one of them to directly view. We’ll add this search in the navigation bar since the user should be able to jump to a particular issue, no matter which page they are viewing.
Assuming that the number of issues is large, it wouldn’t perform well if we were to apply a filter criterion like a regex on all the issues. That’s because to apply the regex, MongoDB would have to scan all the documents and apply the regex to see if it matches the search term.
If you now execute the same find() query to look for documents containing the word click, you should find that it returns two issues, the first one because the term “clicking” was there in the title. The second will also be returned because the description has the term “clicking” in it. Note that this is not a pattern search, for example, searching for the term “clic” is not going to match any documents, even though it partially matches the text in the documents. Also, you will find that common words (known as stop words) like “in”, “when,” etc. are not indexed, and searching for these will result in no matches.
This query should return two documents in the result, the same as when it was executed in the mongo shell. In the next section, we’ll add the UI to use this API and search for documents using a search bar. Note that although the API allows for combining other filter values with the search query, the UI will use only one or the other.
Instead of implementing the search component ourselves, let’s use one of the popular controls available. I have chosen React Select ( https://react-select.com/home ), as it fits the purpose nicely: after the user types in a word, the requirement is to asynchronously fetch results and show them in a dropdown, one of which can be selected. This component’s Async variation lets us achieve this effect easily.
Let’s also create a new component within the UI source directory that will display a React Select and implement the methods required to fetch the documents using the new search filter in the List API. React Select needs two callbacks to show the options: loadOptions and filterOptions . The first is an asynchronous method that needs to return an array of options. Each option is an object with the properties label and value, the label being what the user sees and the value being a unique identifier. Let’s choose the issue ID as the value, and for the label, let’s combine the ID and the issue title.
An instanceId is useful for React Select to identify the control in case there are multiple React Selects being used on the same page. Let’s set this to search-select. Without this ID, you will find that React Select auto-generates these IDs and an error in the console will be shown saying that the server-rendered ID and the client-rendered ID do not match.
We don’t need the dropdown indicator (down arrow on the right side of the control) that can be used to pull down the list of pre-loaded options. Since there are no pre-loaded options, this is not needed. There is no direct option for this, rather, React Select allows each of the components within React Select to be customized using the components property. We’ll just set the DropdownIndicator component to null, indicating that nothing needs to be shown.
The React Select component is designed for selecting and showing the selection once selected. We really don’t need to show the selected item, so let’s just set the value property to an empty string to achieve this.
Although functionally this will work, you will see that the alignment of the search bar in the header is not proper; the search control occupies the entire width of the header and pushes the right side Nav to the next row. To avoid this, we’ll need to wrap the Search component with a <div> or something that restricts the width. Instead of a fixed width, let’s use React-Bootstrap’s Col component , which will flexibly change its width depending on the screen size. Further, as suggested in the React-Bootstrap’s Navbars documentation at https://react-bootstrap.github.io/components/navbar/?no-cache=1#navbars-form , to get the control to properly align within the Nav, we need to enclose it within a Navbar.Form component.

Search control in the Issue List page
In this chapter, we explored a variety of techniques and concepts that you can use to implement common features that make the application more usable.
You first learned about a common React pattern for reusable code: Higher Order Components (HOCs). You then saw how to use MongoDB’s aggregate framework to summarize or even expand the data fetched from collections. You saw how to implement common features, how to use third-party components for undoing a delete and adding pagination, and how to use a search control for finding issues based on a text index in MongoDB.
In the next chapter, we’ll discuss how to go about implementing authentication and authorization for the Issue Tracker application. We’ll use Google Sign-in to let users sign in to the Issue Tracker application. While most of the application will continue to be available for everyone, including those who have not signed in, we’ll make it so that only signed-in users can make any modifications to the data.
GraphQL schema does not allow a variable schema, or objects with fields that are not named in the schema itself. If we return the object stats as is, it would amount to every object having a key (value of the owner field) that is not predefined. There is no way we could define such a schema. GraphQL is all about being able to specify which keys are important to the caller and using a variable key will make it impossible.
A column total is not that simple, you’d have to reduce the entire stats array, and within the reducer function, return an object with totals for each status.
As the documentation suggests, it’s not a good idea to store computed values in the state. Instead, computing them from the original source of truth whenever required is recommended. In cases where the computation can become expensive, utilities such as memorize can be used to cache computed values.
Most applications need to identify and authenticate users. Instead of creating a custom sign-up and authentication mechanism, we’ll integrate with one of the social sign-ins. We’ll only implement one (Google Sign-In for Websites). This will serve as a good example for other integrations, since it uses the OAuth2 mechanism, which most other authentication integrations also use.
We’ll make it so that users can view all information without signing in, but in order to make any change, they will have to sign in. We’ll use a modal dialog that lets the user sign in with Google from anywhere in the application. Once signed in, the application will let the user stay on the same page, so that they can perform the edit functions after having signed in.
In all this, we won’t lose sight of server rendering. We’ll ensure that entire pages can be rendered at the UI server even though they are authenticated pages.
Let’s start by building the necessary user interface for signing in users. Although we won’t do any authentication in this section, we will ensure that all the ground work in terms of UI is in place for adding it in later sections.
In the navigation bar, on the right side, let’s have an item with the label “Sign In”. On clicking this, let’s show a modal dialog that lets the user sign in using a button labeled Sign In. For the Issue Tracker application, we’ll have only one sign-in button, but this approach allows you to add multiple sign-in options like Facebook, GitHub, etc. On successful sign-in, let’s show the user’s name instead of the Sign In menu item, with a dropdown menu that lets the user sign out.
To achieve all this, let’s create a new component called SignInNavItem similar to IssueAddNavItem, which can be placed in the navigation bar. The complete code for this component is shown in Listing 14-1 and I’ll discuss a few important snippets here.
On clicking the Sign In button in the modal, all we’ll do is set the given name of the user to “User1” and the signed in state to true. On signing out, we’ll reverse this. For these, we have the handlers signIn and signOut in the component. Finally, we’ll need to add a bind(this). The final source code for this component including all this is shown in Listing 14-1.

The Sign In modal dialog
Now that we’ve got most of the UI in place, let’s replace the Sign In button with a button to sign in using Google. Once signed in, we’ll use Google to retrieve the user’s name and show it instead of the hard-coded “User1”.
The various options for integrating with Google Sign-In are listed in the “Guides” section at https://developers.google.com/identity/sign-in/web/sign-in . As a preparatory measure, we need a console project and a client ID to identify the application. Follow the instructions in the guide to create your own project and client ID. As for the origin URI, use http://localhost:8000, which is where the Issue Tracker application resides as of now. Once you have done that, save the client ID in your .env file in the UI server directory; this will be needed in the UI code for initializing the Google library. A sample entry in sample.env is shown in Listing 14-3. You will have to use your own client ID in place of YOUR_CLIENT_ID.
The button in the guide automatically creates a project called My Project in the API console. If you want finer control of the names that will be used, create an OAuth2 client ID in the API console at https://console.cloud.google.com/apis/credentials instead.
In the recommended method for integration listed in the guide, the library itself renders the button and handles its enabled and signed-in states. Unfortunately, this doesn’t work well with React because the Google library needs a handle to the button and needs it to be permanent. If you try to use the button within a React-Bootstrap modal, the Google library throws errors. That’s because, on closing the modal, the button is destroyed and recreated when the modal is opened again. The library apparently doesn’t like this. So, we must display the button ourselves by following the guide titled “Customize the Sign-In Button”.
Now we are ready to use the library and implement Google Sign-In within SignInNavItem. The complete set of changes for this component can be found in Listing 14-6, some snippets of which I’ll discuss next.

The Sign In modal dialog with Google button

The application after signing in
Say we wanted to show the user’s profile picture after signing in. Do you think this can be done? How? Hint: Look up the guide for getting profile information in the Google developer site at https://developers.google.com/identity/sign-in/web/people .
Answers are available at the end of the chapter.
Just authenticating with Google is not enough; we’ll need to do something with the authentication. In this section, we’ll ensure that the credentials are verified at the back-end. We’ll also get the user’s name from the back-end to verify that we use only verified authentication information. Later, we’ll establish a session for a logged-in user and keep it persistent across browser refreshes.
Validating a token at the back-end is required as a measure of security. That’s because the back-end cannot trust the UI to have done the authentication, as it is public and can also respond to any HTTP request, not just from the Issue Tracker UI. The technique to do this is described in the guide “Authenticate with a Backend Server,” at https://developers.google.com/identity/sign-in/web/backend-auth . Essentially, the client authentication library returns a token on successful authentication, which can be verified at the back-end using Google’s authentication library for Node.js. We’ll need to send this token from the UI to the back-end for it to be verified.
Let’s create a new file called auth.js in the API server to hold all authentication-related functions. Also, let’s not use GraphQL to implement the signing-in API. One reason is that it is not straightforward to set and access cookies within GraphQL resolvers and we’ll be using cookies in later sections to maintain a session. Another reason is to keep the implementation flexible, and if required, use third-party libraries such as Passport, which hook directly in to Express rather than GraphQL.
So, in auth.js, we’ll implement a series of endpoints as Express routes. We’ll export these routes, which we’ll later mount in the main Express app. The complete code for this file is shown in Listing 14-7, from which I’ll discuss some snippets.
This change will require a restart and a browser refresh (since changes to uiserver.js are not handled by HMR). Now we are ready to send the Google token to the new API. The token itself can be obtained by a call to googleUser.getAuthResponse().id_token, as we saw when manually extracting the token. Then, we’ll need to pass this token to the signin API, gather its resultant JSON, and use the givenName field from there to set the state variable givenName.
All the GraphQL APIs were successfully executed because Apollo Server enabled CORS, as we discussed in Chapter 7. But, this was applicable only to the endpoint prefix /graphql. For the new endpoint prefix /auth, we’ll need to handle it separately. But that’s not all. Since we are going to be setting a cookie in the next section, we’ll need a more elaborate configuration to make this work.
Rather than do all that right now, let’s switch to the proxy mode of operation, since it’s simpler to get it working in this mode. Once all authentication- and authorization-related changes have been made, we’ll switch back to the non-proxy mode and configure CORS correctly in later sections.
This change too will require the UI server to be restarted. After the restart, if you test the sign-in process, you’ll find that the API call to /auth/signin now succeeds (use the Network tab in the Developer Console to verify this) and you’ll find your given name (based on the Google user that you used to sign in) reflected in the navigation bar, as in the previous section. But the difference is that the name is now verified and can also be used in the back-end.
Although we verified the token and used the name from the back-end, we did not persist the information. This means that on a browser refresh, the information about the sign-in would disappear, forcing the user to log in again. Further, any calls to the other APIs will not carry any authentication information, preventing the APIs from applying any authorization restrictions.
One way to persist the authentication information is by creating a session on the back-end, identified by a cookie. This can easily be done by using the middleware express-session, which adds a property in the request called req.session . On this session, variables can be set and retrieved, for example the user’s ID and email. The middleware maintains, in memory, a mapping between the session variable and the cookie, which is also automatically sent to the browser by the middleware.
If the server instance were not a single one (for reasons of scaling or for high availability), the session information will not be shared among the instances, requiring the user to sign in to all of them separately.
The session information is opaquely encoded and cannot be shared across different services, especially those written in different languages or using different technologies.
A server restart will lose the login.
JSON Web Tokens (JWT) solve this problem by encoding all the session information that needs to be stored in a token. This is very similar to the Google token that we received after authenticating with Google. The token string by itself has all the information, that is, the user’s name, email ID, etc. But the information is encrypted so that it cannot be snooped upon or impersonated.
Why not use the Google token itself? Why do we need to generate one of our own? The reason is that if and when you need to introduce other forms of authentication, it is better to have a single token that identifies the user to the Issue Tracker application, uniformly. Further, creating our own token lets us add more variables, for example, a role identifier can be added to the session information and that can be quickly retrieved to apply authorization rules.
In this section, we’ll establish a session that persists even across server restarts. We’ll use JWT to generate a token and send it back to the browser. On every API call that the UI makes, this token will need to be included, identifying the signed-in user.
The UI can keep it in memory and attach the token as a header with every API call request. The common header that is used for this is the Authorization: header. This will work great as long as the user uses the application as an SPA and doesn’t refresh the browser. But on a browser refresh, since the page is reloaded, all JavaScript memory will be reinitialized, making the token unavailable.
The token can be saved in the browser’s local storage or session storage instead of memory, so that it is persisted. But this can be insecure if there exists an cross-site-scripting (XSS) vulnerability in the application. You can read more about this in the OWASP website: https://www.owasp.org/index.php/Cross-site_Scripting_(XSS) . Essentially, an XSS vulnerability is created by forgetting to escape HTML special characters when generating a page, allowing a malicious user to inject code into the application. Since it is not escaped, the code can get executed rather than displayed, allowing access to the local storage data programmatically.
Most modern UI frameworks, including React, avoid XSS by making it extremely hard for a programmer to generate HTML without encoding. But, we have included a few third-party UI libraries such as React-Bootstrap and React-Select, and we can’t be sure how well these have been fortified against XSS attacks.
The token can be sent across as a cookie, and to avoid XSS, we can prevent the cookie from being read programmatically by setting the HttpOnly flag on the cookie. The downside is that the amount of information that can be saved in a cookie is limited to 4KB. Further, there are many restrictions that browsers place on cross-site cookies. Since the Issue Tracker UI and the API servers are different, it is almost impossible to make cookies work if the two servers are under different domains.
You may also read on the Internet that cookies expose your application to Cross-Site Request Forgery (XSRF), and that you need an XSRF token with every request to avoid this. But this is true only for conventional HTML forms.
Storage Method | Pros | Cons |
|---|---|---|
In-memory | Secure; No size limit | Session is not persistent; Including the token in all requests has to be programmatically managed |
Local storage | No size limit | May be vulnerable to XSS attacks; Storage and token inclusion has to be programmatically managed |
Cookie | Simple to implement | Size Limit on data; Cross-domain limitations; Vulnerable to XSRF in HTML forms |
Using cookies to store JWT seems the best option, provided the information stored in the JWT is small enough and the UI and API servers are part of the same domain. Also, we need to store very little information (just the name and email ID, and maybe the role in the future), and most applications are deployed with the UI and the API being sub-domains of the main domain. Further, since the Issue Tracker application has no conventional HTML forms, and the GraphQL API does not accept anything other than application/json as the content type in POST requests, it is not vulnerable to XSRF. But it allows API calls using the GET method, which is vulnerable to XSRF. We’ll need to disable this method of access since the UI does not use it.
As for the JWT_SECRET variable, we’ll need a configuration variable in the environment. Let’s use an environment variable called JWT_SECRET for this. You should generate your own random string as the value for this variable, especially when deploying the application in production.
To ensure that on a browser refresh we continue to maintain the signed-in status, let’s fetch the authentication information using the /auth/user endpoint API. The only component as of now that uses the user information is the SignInNavItem component. So, let’s load this data in that component’s componentDidMount() method and set the user information in its state.
Now, you can test the application and you’ll find that the signed-in information persists across browser refreshes. It would also be a good idea to check if the JWT is being sent across on every GraphQL request as well. You can do this by navigating to a different page and using the Developer Console to inspect the network traffic. You should see that the cookie named jwt is being sent to the server on every request. Also on a browser refresh, you should see a request going out to /auth/user and the menu item in the navigation bar change from “Sign In” to the user’s given name.
If you click on Sign Out, the menu item will change back to Sign In, but on a browser refresh, you will find that the username is back on the menu item. That’s because the cookie is still active, and that indicates a signed-in state.
Signing out requires two things: the JWT cookie in the browser needs to be cleared and the Google authentication needs to be forgotten.
After this set of changes, even on a browser refresh, you should find that the signed-out status doesn’t change. To confirm, you could inspect the network traffic to ensure that the cookie is not being sent on any request. Another way to confirm this is by looking at the cookie data in your browser and ensuring that the site localhost does not have the jwt cookie after a sign-out.
Now that we’ve identified a user who is accessing the Issue Tracker application, let’s put this information to use. A typical enterprise application will have roles and users belonging to roles. The roles will specify what operations are allowed for the user. Rather than implement all that, we’ll just implement a simple authorization rule that suffices to demonstrate how this can be done.
The rule that we will implement is thus: if a user is signed in, the user is allowed to make changes. Unauthenticated users can only read the issues. Thus, we’ll prevent any API under mutation from being accessed by unauthenticated users. We’ll also need to change the UI to disable operations that are unavailable, but let’s reserve that for the next section. In this section, we’ll only ensure that the back-end APIs are secure and prevent unauthorized modification. The APIs will report an error when an unauthorized operation is attempted.
Now, if you try to access any of the functionality from the UI that makes a call to any of these APIs, you should find that they fail with an error, "UNAUTHENTICATED: You must be signed in." For example, clicking on the + button to create an issue and clicking Submit in the modal dialog will result in the error message as a Toast.
If we needed to prevent unauthenticated users from accessing any functionality, that is, the entire application needs to have protected access, how could the GraphQL APIs be changed for this? Ignore the UI changes that would be needed to let users sign in. Focus on the APIs alone. Hint: Look up Apollo Authentication documentation at https://www.apollographql.com/docs/apollo-server/features/authentication.html .
Answers are available at the end of the chapter.
It’s great to prevent users from executing unauthorized operations, but it would be even better to prevent access to these operations at the UI rather than check for them at the back-end alone. In this and the next section, we’ll make the UI aware of the signed-in status.
We’ll use two mechanisms to do this. In this section, we’ll disable the Create Issue button in the navigation bar by conventional means. We’ll lift the state up to a common ancestor and let the state flow down as props that can be used in the children. In the next section, we’ll use a different technique that’s more suitable for passing props to components very deep in the hierarchy.
Let’s choose the Page component as the component in the hierarchy where the user signed-in state will reside. To do this, we’ll have to convert the component into a regular component, that is, not a stateless component. Then, we’ll move the state variable user from SignInNavItem to Page. Instead of separate methods for signing in and out, let’s use a single method called onUserChange, as this makes it easier to pass it down as props. We’ll pass this method and the user variable down to the navigation bar (and later, further down the hierarchy).
Now, on testing, you will find that the SignInNavItem retains its behavior of showing the username when signed in and a Sign In label when not. Further, the Create Issue button gets disabled when the user is not signed in.
Although this seemed like a lot of work for very little effect, this becomes necessary when common properties are shared among sibling components. Note that we could have moved the state up to the navigation bar component NavBar, but since we’ll need the user state passed down to other components that are not under the NavBar, it was more convenient to move it to the Page component.
In this section, we’ll make other components aware of the authentication status. We’ll disable the Close and Delete buttons in the Issue Table and then we’ll disable the Submit button in the Edit page.
But doing this in the same manner as we did for the navigation menu items will not only make it tedious (passing the user property through many components in the hierarchy), but it also poses a challenge. In the Contents component, we are generating the routes from an array, and it’s not clear how to pass props to the component that will be rendered.
So, in this section, I’ll introduce the React Context API that can be used to pass properties across the component hierarchy without making intermediate components aware of it. The React Context is designed to share data that is to be considered global. The authenticated user does fall under the global category, so it’s not a bad idea to use the context for this purpose.
Note that since IssueTable is a child of IssueList, it will receive the user context without having to pass it explicitly through the IssueList component. Also, we don’t necessarily have to set the value of the context in the provider to a static one, as shown. It can be set to the value of a state variable, which will then have the effect of re-rendering all child components when the state changes, with the new value in the context.
This change can be tested independently—you should be able to see that the Submit button in the Edit page is disabled until you sign in.
The next component that needs a change to incorporate the user context is the IssueRow component . But unfortunately, stateless components do not have a this variable, and therefore, the context is not available through this. Earlier versions of React passed the context as an additional parameter to functional components, and it can be done using the Legacy Context API. But this is not recommended, because the old API is deprecated.
Another option is to consume the context at the nearest parent that is not a stateless component and pass the variable down as props. But this defeats the purpose of creating a context in the first place. So, let’s instead convert the IssueRow component to a regular (that is, not stateless) component and consume the context.
There is also another complexity: we’re wrapping the component using withRouter. This causes the wrapped component to inherit the static variable contextType from the inner component (the component that’s being wrapped), and consequently causes an error in the Developer Console. This is because the wrapped component happens to be a stateless component.
To prevent it from throwing this error, we need to delete the contextType static variable in the wrapped component, leaving it in the inner component alone. This issue is present in React Router as of writing this book, but it is possible that it has been resolved at the time you are reading this book and trying it out. Do refer to the details of this issue at https://stackoverflow.com/questions/53240058/use-hoist-non-react-statics-with-withrouter for more information.
With this change, you should be able to see that the Close and Delete buttons are disabled by default until you sign in. You could, if you prefer, revert the changes to the navigation items to use the context instead of passing the user as props via the NavBar component.
We avoided passing props to the components constructed by <Route>s. In the case of the user context, we could do this because the user information can indeed be considered a global variable. What if you need to pass something through to the routed component, but it is not a global variable? In other words, how does one pass props to routed components? Hint: Look up this blog post: https://tylermcginnis.com/react-router-pass-props-to-components/ .
Answers are available at the end of the chapter.
When we added verification of the Google token, we had to switch to the proxy mode of operation because CORS blocked the request from being sent to /auth/signin. In this section, we’ll see how we can get the application to work in the non-proxy mode by relaxing the CORS options, at the same time maintaining security.
The reason the request was blocked is that when the origin of the application (where the starting page, /index.html, is fetched from) is not the same as the target of any XHR call, the browser blocks it, deeming it unsafe. In the Issue Tracker application, the starting page was fetched from http://localhost:8000 but the API calls were being made to http://localhost:3000. At this point, it would help for you to read the section titled “Proxy-Based Architecture” in Chapter 7, “Architecture and ESLint,” as a recap on CORS.
Now, a sign-in will seem to succeed and you’ll be able to see the Sign In menu item changed to reflect the given name. So, adding the default middleware did work, and the browser is sending API requests to /auth routes as well. But there is a caveat. If you refresh the browser, you will find that the authentication information has disappeared!
If you inspect the Network tab of the Developer Tools, you will find that the jwt cookie is not being sent in further requests to the server, and therefore a request to /auth/user returns the signedIn flag as false. But you can see that the cookie was indeed set in the response to /auth/signin.
So, the default CORS configuration seems to allow requests, but does not allow cookies to be sent for cross-origin requests. This makes sense as a safe default because any public resource should be accessible anyway; it is only the authenticated requests that have any user credentials that must be blocked.
All XHR calls (that is, calls using the API fetch()) must include the header credentials: 'include'. Otherwise, cookies will be stripped from these calls.
If this is not done, you will find a helpful message in the Developer Console like this:
It’s not enough if the browser is instructed to send credentials; the server must also explicitly allow it. This is done by another CORS configuration option called credentials, which must be set to true.
Now, on testing you will find that a browser refresh does indeed maintain the logged-in status. But all the GraphQL API calls use the original CORS configuration, which don’t have the credentials yet. This will cause the APIs to reject any modifications from the application. For example, a Create Issue even when you are signed in will fail, saying you need to be logged in.
For the GraphQL API calls to allow these, we’ll need to set the CORS configuration options in the Apollo Server as well. Apart from the true and false values, the CORS option when creating the Apollo Server can also take in the CORS configuration itself. This is what we’ll use to set the CORS configuration.
With all these changes, you will find that credentials are being sent to the server for all GraphQL calls as well. At this point, the application should work as it was working in the proxy mode of operation at the end of the previous section. You should also take a look at the Network tab in the Developer Tools and verify that the cookie is being set on an authentication response and sent to the API server for /auth and /graphql calls.
Until now, we did not handle the server rendering of pages including the authenticated user information. The effect of this is that on a browser refresh, the page is loaded as if the user has not signed in. The menu item says “Sign In”. Then, after the user credentials are fetched asynchronously and the user state is updated, the menu changes to the given name of the signed-in user.
In practice, at least for the purpose of search engine bots, this is perfectly fine. That’s because there is no way search engines are going to sign in as a user and crawl the site’s pages. It is only the publicly available set of pages that a bot crawls.
But if you don’t like the (slightly) ungainly flicker which transitions from an unsigned-in state to a signed-in state, or, if you like all pages to behave consistently whether or not the user is signed in, you may want to consider rendering the page at the server that includes the credentials.
The initial data fetched for the user credentials goes to the /auth endpoint rather than the /graphql endpoint. Server rendering relies on the fact that all data fetching calls go through graphQLFetch(), where we make critical decisions based on whether the call is made from the browser or the UI server.
When the user data is fetched, the API call made by the UI server to fetch the data must include the cookie. When called from the UI, the browser would have added this cookie automatically. But in the UI server, we’ll need to manually include the cookie. Otherwise, the call will behave as if the user is not signed in.
The initial data that is fetched needs to be in addition to any other data fetched prior to rendering using fetchData() functions in the views. Also, this data is not fetched by any view which is part of the routes: it is fetched at a much higher level, in the Page component.
To address the first challenge, let’s introduce a new GraphQL API for the authenticated user credentials. We used the /auth set of routes because GraphQL resolvers could not set cookies, as they did not have access to the Express response object. But fetching the signed-in user’s credentials only requires access to the Express request object, and as we saw in the Authorization section previously, this should be feasible.
The next challenge is to let the user credentials pass through to the API calls via the UI server. You will recall that a server rendered page will be returned when a request is made from the browser to a routed URL such as /issues or /about to the UI server. The UI server then makes API calls to the API server and prefills the page using the data. So, the cookie received in the first step by the UI server needs to be replicated in calls to the API server.
Essentially, any call to graphQLFetch() made as part of rendering from the server needs to include the JWT cookie that was received in initiating the request. The way we can do this is by letting all fetchData() static functions receive an optional parameter cookie, which can be passed through when any function calls graphQLFetch().
Since this function is being used both on the server as well as on the browser, we’ll have to ensure that the cookie argument is passed only for calls from the server rendering routines, that is, from render.jsx. For calls from the browser, the cookie will automatically be included by the browser.
We don’t really need to change all the other fetchData() calls such as IssueList.fetchData to include the cookie. That’s because the presence of a cookie does not make a difference to the result of these publicly available APIs. There may be cases where this is required, for example, if the list of issues returned depends on the currently signed-in user. In such cases, the relevant fetchData() functions also need to be modified to be able to pass through any cookies.
To address the next challenge of fetching the user data in addition to other data required for routed views, let’s decide that all global data fetches such as user credentials will have to be hard-coded while rendering on the server. We will not make this as generic and flexible as what we did for the routed components. So, while rendering on the server, let’s make a call to Page.fetchData() explicitly to fetch the global data and include it in the store.
Note that we included the cookie in all fetchData() requests, including the active routed component’s fetchData() call. As discussed, this is not required because these calls will not be affected by the presence of credentials. Yet, to keep it consistent and to let future modifications of views that may use the credentials display the contents differently, let’s keep this.
Now, if you test the application, you will find that the flicker of the menu item is no longer there. Although this is not a great use case to be solved with all the complexity that was introduced, it could serve as a pattern that can be used to include truly global data at the time of server rendering.
In the previous two sections I glossed over an important aspect of cookies, which can come into play when the APIs are accessed directly from the browser. Since we used localhost as the domain to access the application, it worked seamlessly.
It is important to note that cookies and CORS work slightly differently when it comes to cross-site requests. Whereas CORS recognizes even a port difference as a different origin, a cookie set by the server is tied to the domain, and all requests to the same domain from the browser will include the cookie. For example, a cookie set by localhost:3000 will be sent to localhost:8000 as well. The cookie policy ignores differences in ports.
If you inspect the network traffic in the developer tools, you will find that the JWT cookie is being passed to requests to localhost:8000 (the UI server) even though it was set by the signin API by the API server on port 3000. This is what made it possible for us to pass the credentials through the UI server to the API server during server rendering.
(On MacOS and Linux, this file can be found at /etc/hosts, and it’s found at c:\Windows\System32\Drivers\etc\hosts on a Windows PC.)
After this, you’ll find that authentication and authorization still do not work. In particular, a sign-in will work, but a browser refresh will lose the credentials.
Now (after restarting the servers, since the environment variables have changed), you will find that the authentication works fine, because cookies set in the API calls are used in requests to the UI server.
But, if you need different domains, for example, if you need to access the application using localhost:8000 while the API is at api.promernstack.com:3000, you will find that the credentials are not sent to the API server. This is because the domains are different (localhost vs. promernstack.com). It is going to be extremely rare to require that the two servers be on two different domains (not sub-domains). But if it is really needed, the best option you have is to use the proxy mode of operation, where the browser sees only one domain for both the API and the UI.
There are many ways to authenticate users and different applications have different needs. For the Issue Tracker application, we used Google to authenticate a user. A different approach would be needed for signing up and authenticating users using say, a user ID, but once authenticated, the rest of the concepts you learned in this chapter would still be applicable.
You saw how JWT can be used to persist session information in a stateless, yet secure manner. Then, you saw how authorization works with GraphQL APIs and how it can be extended to perform different authorization checks based on the application’s needs. You also saw how CORS and cookie handling restrictions on the browser come into play when the browser accesses the APIs directly.
All this while you hosted the application on your own computer. To make the application available to other users, it needs to be hosted on an external server. In the next chapter, we’ll take a look at how this can be done using a Platform as a Service (PaaS) on the cloud, Heroku.
Yes, the profile picture is available and can be obtained by a call to the getImageUrl() function of the basic profile.
This way, the call doesn’t even reach any resolver function.
There are quite a number of ways to deploy the Issue Tracker on the cloud. You have do-it-yourself options such as firing up your own Linux-based instances on Amazon AWS, Google Cloud, and Microsoft Azure and running Node.js and MongoDB on them just like you did on your local computer throughout this book.
But I find that Platform-as-a-Service (PaaS) options are far easier to deploy to and maintain. So, in this chapter, I have chosen one of the most popular PaaS, Heroku. There is a free tier that you can experiment with before making a choice for your production application. In this chapter, I’ll guide you through deploying the Issue Tracker application on Heroku.
The simplest way to deploy an app on Heroku is using a Git repository . Until now, we haven’t discussed how the Issue Tracker source code can be controlled and shared among a team for collaboration. You’d have created all the files on your local computer and very likely not used a repository as yet. Now is a good time to do that, since we’ll need to do that anyway for deploying on Heroku. You could use CVS, SVN, Git, or any other modern source code control system, but since Heroku needs Git, we’ll use the same for the Issue Tracker application’s collaboration needs as well.
The repositories we are about to create should not be confused with the repository accompanying this book, at https://github.com/vasansr/pro-mern-stack-2 . The book’s repository has been organized in a manner to make it convenient to follow the code changes in the book and it is not ideal for real-life applications. So, do not clone or fork this repository; instead, start fresh with the code that we have written until this point.
You could use GitHub or BitBucket or any cloud Git service, or even your own hosted Git server. I have assumed GitHub for the instructions that follow. The instructions for the other services will be similar, if not identical.
Let’s start by creating these repositories in GitHub. Log in to GitHub at https://github.com (create an account if you don’t have one) and, using the user interface, explore how to create repositories. We have two deployable applications: the API server and the UI server. We will need two repositories, one for each of these. Let’s call them tracker-api and tracker-ui. You will also need the Git command line utility: git. I will not go into the details of how to install git or set it up for accessing GitHub. There are different options to do these, and you will find a number of resources on the Internet as well the GitHub website itself that will help you get set.
There are two options to access the GitHub repository from the Git command line: using SSH or using HTTPS. In the following instructions, I will assume that you have set up Git for access using SSH. If you prefer HTTPS, you will have to change the remote URL for the repository in the following commands accordingly.
In this chapter, there will be many situations where you need to use your GitHub username. It would be convenient to define an environment variable so that commands can be copy-pasted from either the book or the book’s GitHub repository without modification. Let’s use an environment variable called GITHUB_USER for this purpose. At this point in time, you’ll need to set this variable to your GitHub username.
The following commands assume a Linux computer or a Mac, and the environment variable is accessed using $GITHUB_USER. On a Windows PC, you will need to use %GITHUB_USER% instead, or replace the variable name with your GitHub username. The same applies to other variables that we will be using in this chapter.
Adding a remote in Git sets up a linkage between the local repository and a remote repository, which in this case is on GitHub. After it succeeds, you should be able to see the source code in the GitHub website. Check it out and make sure all the code, including the script subdirectory, has been created.
At this point, you could browse the UI repository online on GitHub to ensure that the files have indeed been pushed to GitHub.
Before we deploy the servers on Heroku, let’s first ensure that we have a MongoDB database on the cloud. Do revisit the section entitled “Installation” in Chapter 6, “MongoDB”. If you have already been using a MongoDB database on the cloud, there is nothing more to be done. If not, choose one of the cloud options described in that chapter and follow the instructions to create a database on the cloud.
The first thing to do is create a Heroku account, if you don’t already have one. This can be done starting at https://heroku.com . Once you have a login account, you can install the Heroku CLI from https://devcenter.heroku.com/articles/heroku-cli#download-and-install . Most of the commands that we’ll be executing for deploying are also available from the Heroku web user interface. But using the CLI, it’s easier to follow the instructions in this book and execute commands.
In the following sections , we’ll create and deploy the API application and then the UI application on Heroku.
Deploying the API application requires a few changes to the application, because of what Heroku expects.
Firstly, the port on which the application can listen is dynamically allocated by Heroku. The reason is that each application is deployed in a container rather than a dedicated host. Thus, Heroku assigns a port to the application and sends traffic to that port for the application. But, on the Internet, the same port is reflected as an HTTP (80) or an HTTPS (443) port. There’s a firewall in Heroku that does this. Heroku then sets an environment variable to let the application know which port the traffic will be received in the container. This environment variable is called simply PORT . And that’s the port the application must listen on.
Instead, if it shows an error that the application name is already in use, you will have to try different names for the application. When it succeeds, the creation will add a Git remote repository on Heroku, which will be referred to locally as heroku, pushing to which will have the effect of deploying the application.
This command assumes a Linux or a Mac computer. When using a Windows PC, you will have to use the %DB_URL% syntax for variables and type the entire command in one line rather than use multiple lines with the \ character at the end.
This means that Heroku, after copying over the files from the Git repository, has also run npm install on the target directory. Since we have package.json and package-lock.json files also in the Git repository, the versions that it installs will also automatically match what you have used during development.
Heroku installs packages listed in devDependencies also, but later prunes these, as can be seen by the message Pruning devDependencies.
If everything works okay, you should see the last line that says that the deployment is verified and done. Now you can test the API by using the Playground. This will also ensure that the API server can connect to the MongoDB server on the cloud. To access the Playground, browse to https://tracker-api-$GITHUB_USER.herokuapp.com/graphql (replace $GITHUB_USER with your GitHub user ID).
You could also just type heroku open in the console, and a browser tab or window should open with the previous URL, but without the /graphql path, resulting in a message “Cannot GET /”. You will need to append /graphql once the window opens to access the Playground.
In the API application, the source code was enough for the server to start and run the server . The UI application is a different because it needs compilation before the files needed to start the server are ready. There is also the need to link or copy Bootstrap’s static CSS and JavaScript files to the public directory.
There are two npm scripts that this can be done in. The script postinstall is one that is run right after npm install finishes. This is a Node.js specific script, and it will be run automatically by npm. Thus, it takes effect both when a developer runs npm install locally as well as after Heroku runs npm install after deployment. The other script is heroku-postbuild, which is specific to Heroku. That is, this script is run only on Heroku deployments, and not when a developer runs npm install on the local computer.
Now, if you browse to the applications URL (or, simply type heroku open while in the ui directory), you should see the Issue Tracker UI with the initial set of issues loaded. You should also be able to navigate to other pages successfully. These other pages should also work on a browser refresh while on that page.
But you will find that this works only for unauthenticated users. Signing in at this point will not work because of the new domain being used and due to CORS and cookie considerations. But before we address that, let’s see if the proxy mode works.
Now, if you try the application, the first page load will succeed, but on navigating to other views, you will see browser-rendered requests will time out, and in the Network tab of the developer tools, you will find that the API calls are failing. If you try the Playground using the proxied URL https://tracker-ui-$GITHUB_USER.herokuapp.com/graphql, you will be shown an error page, again because proxied requests to the API server fail. This happen because of how Heroku routes HTTP requests.
In reality, many web applications share the same resources on Heroku. Not just computing resources, they also share IP addresses. Because of this, it is necessary for every request to specify which application the request should land on so that it can be routed to the appropriate application. This is done using the Host header in the HTTP request. The technique is referred to as Name Based Virtual Hosting, which is also supported by popular web servers such as Apache and nginx.
Browsers automatically set this header to the hostname part of the URL, so any request to the API server or the UI server from the browser was routed by Heroku to the correct application. But when proxying requests, the http-proxy-middleware does not automatically do this. What it does by default is use the original Host header that was received from the browser and copy it to the request to the API server.
The browser resolves the IP address of tracker-ui-$GITHUB_USER.herokuapp.com, which is one of many common IP addresses shared by all Heroku applications.
The browser sets the Host header to tracker-ui-$GITHUB_USER.herokuapp.com.
Heroku looks at the Host header and routes a request to the UI server.
The proxy middleware intercepts the request (because the target is /graphql), and tries to forward this to tracker-api-$GITHUB_USER.herokuapp.com.
The proxy middleware resolves the host for the API server, which also results in a common IP address.
The proxy middleware forwards to the IP address, the same request, that is, with the Host header as tracker-ui-$GITHUB_USER.herokuapp.com.
Heroku receives the request, looks at the Host header, and routes this to the UI server! This leads to an infinite loop until the request times out.
The reason proxy mode worked when we tried it on our local computer is that there was no virtual host based routing. All requests to http://localhost:3000 directly landed in the API server and it did not cause any problem.
Now, if you commit these changes and push them to Heroku using git push heroku master, you will find that the application works great. You should confirm that authentication works and is persisted on a browser refresh.
The non-proxy mode of operation doesn’t work even though the two applications have the same domain (heroku.com). The browser does not share the cookie between these two applications because heroku.com is listed in the Public Suffix List. This list is meant exactly for situations like this, and you can learn more about this at https://publicsuffix.org/learn/ .
Although it seems inconvenient at first, you probably realize by now that if your two applications could share the same cookie, it would also be shared among all other applications on Heroku, even those belonging to other Heroku users. Surely, this is not secure at all. So, Heroku has ensured that the domain herokuapp.com is considered on par with a top-level domain by adding it to the Public Suffix List. This causes the browser to reject a set-cookie header with the domain as herokuapp.com.
The only way to share cookies between the UI and the API server is by using a custom domain. Then, sub-domains on that domain can be used as the UI and API servers, thus enabling sharing of cookies to make the non-proxy mode of operation work. So, you will have to create a custom domain for yourself to try out the non-proxy mode of operation on Heroku.
There are many ways to register your own domain , including free ones. Pick one and create a domain. Once you have done that, create an environment variable called CUSTOM_DOMAIN and set it to that domain. For example, if you have registered myfreedomain.tk as your custom domain, set CUSTOM_DOMAIN to myfreedomain.tk, including the top-level domain .tk. We’ll use ui.$CUSTOM_DOMAIN and api.$CUSTOM_DOMAIN as the UI and API application’s sub-domains in the commands that follow.
One downside of using custom domains is that Heroku does not enable SSL for these by default, or for free. You will need to upgrade to a paid account on Heroku to be able to enable SSL for your custom domain. So, for the rest of this chapter, we’ll use http:// rather than https:// based URLs.
Firstly, you will have to authorize the new domain-based URL as an authorized JavaScript origin in the Google Developer project. The origin will be http://ui.$CUSTOM_DOMAIN. This will also require you to add an authorized domain as $CUSTOM_DOMAIN in the Google developer console in the OAuth Consent screen tab. Note that after adding the JavaScript origin, it takes some time for it to take effect. If you plan on using SSL in the future, it may be a good idea to add the https:// version of the origin now itself.
As the console output instructs, you need to configure DNS so that a request to ui.$CUSTOM_DOMAIN lands at the Heroku hosted UI application. This needs to be done in your domain provider, using their UI. You will need to create a CNAME record, which creates an alias for a domain. Essentially, you’ll need to map the custom domain as an alias for the real domain, for example, sheltered-tor-u2t67pge87ki9sbr6iqw1h.herokudns.com, that Heroku automatically assigned for the application.

Screenshot of Domain Manager after creating a record for ui and api
The servers would now be automatically restarted, and if you test the application, it should work seamlessly as it did in the proxy mode.
Although we made the Issue Tracker application work on your local computer, deploying it to the cloud, especially to a Platform-as-a-Service, introduced some challenges.
In the proxy mode, we had to set up the http-proxy-middleware correctly to change the Host header when proxying requests. In the non-proxy mode, you learned that using the default application domain does not work, and we had to use a custom domain. We also had to make changes to the code, but these changes were compatible with the application for local development as well.
We touched upon Git and version control, and we used two remotes for the repository: one for team collaboration on GitHub and another for Heroku. This is just one way of managing versions and releases. If you are working in a team and want to collaborate, you would do so in the GitHub remote, and when the time comes to release, you would push it to the Heroku remote. I did not delve into other options for managing releases, as these are both specific to your project as well as not really a MERN subject.
As we come to the end of this book, let’s wrap up by discussing what more can be done with the MERN stack in the next chapter. We’ll stop changing the application and just look at other techniques and libraries that are related and could be useful to you in your MERN project.
I hope by now that I have succeeded in planting the fundamentals of the MERN stack in your mind. More importantly, I hope I have equipped you to take it all to the next level, because this is by no means the end of the Issue Tracker application, or anything else you had in mind as a project. If you’d really tried answering the exercises in each chapter, you should now be familiar with where to get more information when needed.
There are many more features that can be added and more technologies that can be used to help you going forward. In this chapter, I’ll touch upon some of the technologies that you may want to consider if you decide to use MERN in real-life projects. But this will only be a brief introduction to what’s possible; we won’t be adding any code to the application we’ve created until now.
Do note that these new things may not necessarily suit your application. You need to evaluate them carefully if and when you hit a roadblock or want to automate some repetitive code as your application grows. The previous chapters in this book should have given you enough confidence that you can use the MERN stack and solve all problems by hand, or yourself. But in many a case, others have faced similar problems and created libraries to solve them. I suggest that you look for an existing solution, but wait until you are clear about what is it that you want solved.
Most technology stacks using relational databases can be supplemented with Object Relation Mapping (ORM) libraries. These add a layer of abstraction and let the developer see objects as such and not as tables with rows and columns.
Schema definitions: This is useful since MongoDB does not enforce a schema, unlike SQL databases. With SQL databases, a schema error will be automatically caught by the database, whereas we ignored schema validations for the Issue Tracker application. Using Mongoose, you can define schemas and have new documents automatically validated against it. As of version 3.6, MongoDB itself supports schemas, but the support seems primitive compared to Mongoose.
Validations: Mongoose has built-in validators over and above what GraphQL does in terms of required checks and data type checks. These include string lengths and minimum and maximum checks for numbers. Further, you can add custom validators such as email ID validators and reuse them across object types.
Isomorphic: There’s a browser component that lets the schema validations be used in the browser. This can help usability by showing errors in the UI before the user submits a form.
Models: Although we encapsulated all code related to issues in issue.js, we did not attach functions to the Issue object. Using Mongoose, you can write truly object-oriented models that can encapsulate data as well as methods in the objects. Models also let the developer write code more intuitively, for instance, use Object.save() rather than db.collection.insertOne().
For smaller projects such as the Issue Tracker , Mongoose was probably not needed. You could easily extract and share the validations in issue.js to reuse code if required. But for larger projects with multiple people working in a team, using Mongoose will definitely avoid errors in the development process and serve as documentation for the object schema, which is especially helpful for newcomers to the team.
If you have read about React, it is very likely that you have also heard about Redux and/or the Flux pattern. Due to its popularity, it’s very tempting to dive right in and start using it. But let’s first look at opportunities for improvement before we find solutions for them.
When we added user sign-in, we found that we had to transfer user information and the sign-in action up the component hierarchy and back down to the components that had use for it. It seems a little wasteful (and an unwanted increase in coupling) for the components somewhere in between, that don’t need to know the information. For example, the component NavBar by itself had little use for the user’s name or signed-in state. All it did was pass the knowledge down to SignInNavItem.
We solved the problem by using React Context for truly global state variables. Further, for initialization, we also created a global store object. If you do run into cases where there is a need to share the state among many components, yet, the state is not really global, you will feel the need to add more and more into the store object. But anything that’s global needs some restrictions or contracts to avoid havoc caused by uncontrolled changes. This is what the Flux architectural pattern tries to define.
When a user interacts with a React view, the view sends an action (usually represented as a JavaScript object with some fields) through the dispatcher, which notifies the various stores that hold the application’s data and business logic. When the stores change state, they notify the views that something has updated. This works especially well with React’s declarative model, which allows the stores to send updates without specifying how to transition views between states.
Essentially, the pattern consists of formally defined actions, for example, Create Issue, initiated by the user. The action is dispatched to a store. This affects the store’s state. Typically, a function called a reducer is used to describe the effect of an action on the state: the new state is a function of the current state and the action.
Redux and Mobx are two popular choices recommended by the authors of React that can be used for global state management, and they follow the concepts of the Flux pattern largely. The effect of these frameworks or the Flux pattern is that you will have to write a lot of boilerplate code, that is, code that looks very much like some other code, seemingly unnecessarily. For every user action, you must formally define an action, a dispatcher, and a reducer that, given the current state, returns the contents of a new state.
Let’s take the action of deleting an issue as an example. You must define a set of formal actions, including a constant like DELETE_ISSUE. Then, you must define a reducer, which is a function that takes in various actions and their parameters and returns a new state (a switch-case for each different action). Then, you must create a dispatcher, which converts the action and parameters into a real action, such as sending the request to the server.
All of this is worth it if the state machine in your application is very complicated, like say, if a delete request can be initiated from several different places in the UI (and even from outside the UI, as in some other user’s action), and it has many other implications besides just removing a row in a table. Not many applications face this kind of complexity.
I can assure you with reasonable confidence that there’ll be a point in time when your application will grow big enough within a single page (imagine your Facebook page) and you’ll know you need Redux or Mobx, and why. Until that point in time, it may be wiser to get things done using just the basics and fundamentals while continuing to learn about these new patterns.
We did a lot of work to set up the React compilation and development environment using Webpack and Babel. If you are not interested in setting up all this or do not want finer control of optimizing and customizing it, you may want to consider application initializers.
This creates a pure React application, that is, it has no server-side components. What this means is that there is no Express server created and, therefore, you cannot do things that the Issue Tracker UI server did: proxying requests and rendering on the server.
This can be used for the UI server only. The API server will need to remain the way it is in the Issue Tracker application.
If you do need the proxy server and server rendering, you could start with Create React App and “eject” the resulting application, by executing npm run eject and customizing the configuration by installing Express, etc. Ejecting the created app has the effect of making all the configuration visible and allowing customization, but it prevents you from easily upgrading to newer versions of Create React App.
Create React App uses Webpack for handling all assets, including CSS. Since the Issue Tracker relied on React-Bootstrap and we don’t have modularized CSS, this is not quite ideal, though it can be made to work by including bootstrap’s CSS file. For an example, see in the source file index.js, how index.css has been included.
Thus, if your requirement is a pure React application, or if you are comfortable making configuration changes to include Express for the UI server, you could use Create React App as your starting point. Or, you could check out some of the popular alternatives to consider based on the type of your project, at https://github.com/facebook/create-react-app#popular-alternatives .
If you want to get a good head start on most of the popular practices followed in a MERN stack application, it’s handy to start with a project that’s already crafted with all of the boilerplate code, as well as an example set of objects that you can tweak or copy to get your job done quickly.
The application mernApp does not use Bootstrap or React-Bootstrap. It has its own style sheets for styling its content. But including Bootstrap is not that hard, and it can be done following the steps that we did as part of the chapter on React-Bootstrap (Chapter 11).
mernApp uses Mongoose and Redux, two technologies we discussed in previous sections, but did not use in the Issue Tracker application.
There is no authentication or session handling in the mernApp.
The code is organized as modules, which are cohesive pieces of code that work together to expose a significant functionality. The only module that is created by default is the Post module and you can create more modules as necessary.
The server is a single server, unlike the Issue Tracker, where we had separate API and UI servers.
mernApp uses REST-based APIs, and not GraphQL.
Despite all these differences, the project does show promise. But it’s a bit outdated, does not work on Windows, many warnings are thrown up during creation of the application, and it is not actively maintained. But version 3.0 is in the making. Perhaps when the new version is released, it could become the application initializer for MERN-based applications.
When you need to integrate more authentication providers such as Facebook or Twitter, if you follow the approach we took in Chapter 14, “Authentication,” you’d have to write different branches of code for each authentication option.
The Node.js package Passport ( http://www.passportjs.org/ ) solves this problem by creating a single framework into which multiple authentication strategies can be plugged. Passport by itself only dictates the framework and the interface to the application developer. Each strategy (for example, the Google Strategy) is implemented as a separate package.
Note that Passport is a back-end-only package. All authentication requests from the UI will need to be passed through the back-end. This is unlike what we implemented as part of the Google Sign-in in the Issue Tracker, where we used Google’s client-side libraries to initiate the authentication request directly to Google’s authentication engine. Once authentication succeeded, we passed the Google Authentication token from the UI to the back-end where it was verified.
In contrast, Passport uses the Open ID Connect protocol that Google supports as an alternative ( https://developers.google.com/identity/protocols/OpenIDConnect ). In this approach, the UI makes a call for authentication to the application’s back-end and not Google’s authentication engine. The user is then redirected to the Google Accounts page, as opposed to it being a pop-up. Then, using a set of callback URLs to the application’s back-end, success and failure of authentication needs to be handled.
The Open ID approach suits server-rendered applications and applications where the user needs to be logged in from the start. For an SPA like the Issue Tracker, this will cause a few refreshes of the application’s page. In comparison, the direct approach used in the Issue Tracker application causes no browser refreshes and the authentication information is updated within the page. But this is a minor inconvenience compared to all the ease of implementation that Passport provides when dealing with multiple authentication providers.
I hope it has been a fun sail through the waters of the MERN stack for you, as it has been for me. I have learned a lot by just contemplating on the programming model, the paradigm, and the new thinking that the MERN stack has opened my eyes to.
I made sure we got into the nuts and bolts of each piece in the MERN stack and the accompanying tools rather than use frameworks like Passport or Create React App that could have made the job easier. I hope you enjoyed getting your hands dirty and the consequent learning that came out of it all, though it was harder to get the job done.
But this is nowhere close to the end. In a few months, I am reasonably certain that things won’t be the same. Who knows? Browsers may themselves adapt or incorporate the virtual DOM technique, making React mostly redundant! Or, you’ll see a new framework (not a library) that anchors React as the View part in an MVC offering. Or, a new version of a library that we used may come up with a totally new way of doing things.
The key thing is to keep looking for these new developments, yet be very, very analytical about why they do or do not work for your application and your team.
Here’s to looking ahead to the more awesome future.