Earlier this week, I presented a webinar on developing web and mobile applications using the MERN and MEAN stacks – the replay and slides are now available.
Details
Users increasingly demand a far richer experience from web applications – expecting the same level of performance and interactivity they get with native desktop and mobile apps.
At the same time, there’s pressure on developers to deliver new applications faster and continually roll-out enhancements, while ensuring that the application is highly available and can be scaled appropriately when needed.
Fortunately, there’s a set of open source technologies using JavaScript that make all of this possible.
Join this webinar to learn about the two dominant JavaScript web app stacks – MEAN (MongoDB, Express, Angular, Node.js) and MERN (MongoDB, Express, React, Node.js).
These technologies are also used outside of the browser – delivering the best user experience, regardless of whether accessing your application from the desktop, from a mobile app, or even using your voice.
By attending the webinar, you will learn:
What these technologies and how they’re used in combination:
NodeJS
MongoDB
Express
Angular2
ReactJS
How to get started building your own apps using these stacks
Some of the decisions to take:
Angular vs Angular2 vs ReactJS
Javascript vs ES6 vs Typescript
What should be implemented in the front-end vs the back-end
This is the fifth in a series of blog posts examining technologies such as ReactJS that are driving the development of modern web and mobile applications.
Modern Application Stack – Part 1: Introducing The MEAN Stack introduced the technologies making up the MEAN (MongoDB, Express, Angular, Node.js) and MERN (MongoDB, Express, React, Node.js) Stacks, why you might want to use them, and how to combine them to build your web application (or your native mobile or desktop app).
The remainder of the series is focussed on working through the end to end steps of building a real (albeit simple) application – MongoPop. Part 2: Using MongoDB With Node.js created an environment where we could work with a MongoDB database from Node.js; it also created a simplified interface to the MongoDB Node.js Driver. Part 3: Building a REST API with Express.js built on Part 2 by using Express.js to add a REST API which will be used by the clients that we implement in the final posts. Part 4: Building a Client UI Using Angular 2 (formerly AngularJS) & TypeScript completed the MEAN stack by adding an Angular 2 client.
This post is similar to Part 4 except that it uses ReactJS rather than Angular to implement a remote web-app client for the Mongopop application – completing the full MERN application stack.
ReactJS (recap)
MERN Stack architecture with React
React (alternatively referred to as ReactJS), is an up and coming alternative to Angular. It is a JavaScript library, developed by Facebook and Instagram, to build interactive, reactive user interfaces. Like Angular, React breaks the front-end application down into components. Each component can hold its own state_and a parent can pass its state down to its child components (as _properties) and those components can pass changes back to the parent through the use of callback functions. Components can also include regular data members (which are not state or properties) for data which isn’t rendered.
State variables should be updated using the setState function – this allows ReactJS to calculate which elements of the page need to be refreshed in order to reflect the change. As refreshing the whole page can be an expensive operation, this can represent a significant efficiency and is a big part of what makes React live up to its name as “reactive”.
React components are typically implemented using JSX – an extension of JavaScript that allows HTML syntax to be embedded within the code.
React is most commonly executed within the browser but it can also be run on the back-end server within Node.js, or as a mobile app using React Native.
JSX & ReactJS
It’s possible to implement ReactJS components using ‘pure’ JavaScript (though, we’ve already seen in this series that it’s more complicated than that) but it’s more typical to use JSX. JSX extends the JavaScript syntax to allow HTML and JavaScript expressions to be used in the same code – making the code concise and easy to understand.
Components can be implemented as a single function but in this post a class is used as it offers more options. The following code implements a very simple component:
By extending React.Component, we indicate that the class implements a component and that the render() method returns the contents of that component
The enclosing component can pass data down to this component as properties (accessed within the component as this.props); in this case, there is just one – name. JavaScript can be included at any point in the returned HTML by surrounding it with braces {this.props.name}. The enclosing component would include this code within its own render() method, where userName is part of that component’s state.:
<HelloMessage
name={this.state.userName}
/>
The state data member for a component should include all of the variable values that are to be rendered (apart from those that have been passed down as properties). State values can be initialized directly in the class’s constructor function but after that, the setState({userName: "Andrew"}) method should be used so that ReactJS knows that any elements containing userName should be rerendered.
JSX gets compiled into JavaScript before it’s used (this post uses the Babel compiler) and so there are no special dependencies on the browser.
Downloading, running, and using the Mongopop ReactJS application
The back-end application should be installed & run in the same way as in parts 2 & 3:
git clone git@github.com:am-MongoDB/MongoDB-Mongopop.git
cd MongoDB-Mongopop
npm install
npm run express
Run the ReactJS client by browsing to http://<back-end-server>:3000/react.
Unlike the Angular client, the ReactJS application is developed and built as a separate project, and then compiled results are copied to public/react in the back-end server (this is covered in the next section).
Build and deploy
To access the source and build an updated version of the client, a new GitHub repository must be downloaded – MongoDB-Mongopop-ReactJS:
git clone git@github.com:am-MongoDB/MongoDB-Mongopop-ReactJS.git
cd MongoDB-Mongopop-ReactJS
As with the back-end and the Angular client, package.json includes a list of dependencies as well as scripts:
Before running any of the software, the Node.js dependencies (as defined in package.json must be installed into the node_modules directory):
npm install
To compile the JSX code, start the development server, and run the ReactJS client, run:
export PORT=3030 # As Express is already using 3000 on this machine
npm start
This should automatically open the application within your browser. Note that the ReactJS code was loaded from a local development server but it will use the real REST API running in the back-end.
Note that when running in this mode, you may get errors when your browser tries accessing the REST API – this is because browsers typically block cross-site scripting. To work around this, install this extension from the Google Chrome store.
If you make changes to the ReactJS client and want to include them in the real back-end then build a new, optimized version:
npm run build
The contents of the MongoDB-Mongopop-ReactJS/build folder should then be copied to MongoDB-Mongopop/public/react.
To see exactly what react-scripts is doing for these operations, review the scripts in node_modules/react-scripts/scripts.
Component architecture of the Mongopop ReactJS UI
Most ReactJS applications are built from one or more, nested components – Mongopop is no exception:
ReactJS components making up the Mongopop client app
The top-level component (MongoPopContainer) renders the “Welcome to MongoPop” heading before delegating the the rest of the page to seven sub-components.
MongoPopContainer is implemented by a JSX class of the same name. The class contains the state variables for any information which must be used by more than one sub-component (e.g. the collection name). It also includes handler functions that will be used by sub-components when they make changes to any state variable passed down. The class implements the render() function which returns the expression that ReactJS must convert to HTML for rendering; in addition to the opening <h1>Welcome to MongoPop</h1>, it includes an element for each of the sub-components. As part of those element definitions, it passes down state variables (which the sub-component receives as properties):
Changes to a data value by a parent component will automatically be propagated to a child – it’s best practice to have data flow in this direction as much as possible. If a data value is changed by a child and the parent (either directly or as a proxy for one of its other child components) needs to know of the change, then the child triggers an event. That event is processed by a handler registered by the parent – the parent may then explicitly act on the change, but even if it does nothing explicit, the change flows to the other child components.
Each of the sub-components is implemented by its own JSX class – e.g. CountDocuments.
Mongopop is a reasonably flat application with only one layer of sub-components below MongoPopContainer, but more complex applications may nest deeper and reuse components.
This table details what data is passed from MongoPopContainer down to each of its children and what data change events are sent back up to MongoPopContainer (and from there, back down to the other children):
Flow of data between ReactJS components
Child component
Data passed down
Data changes passed back up
ServerDetails
Data service
ConnectionInfo
Data service
CollectionName
Data service
Collection Name
AddDocuments
Collection Name
Data service
CountDocuments
Collection Name
Data service
UpdateDocuments
Collection Name
Data service
Sample data to play with
SampleDocuments
Collection Name
Sample data to play with
Data service
What are all of these files?
To recap, the files and folders covered earlier in this series (for the back-end, under MongoDB-Mongopop folder):
package.json: Instructs the Node.js package manager (npm) what it needs to do; including which dependency packages should be installed
node_modues: Directory where npm will install packages
node_modues/mongodb-core: Low-level MongoDB driver library; available for framework developers (application developers should avoid using it directly)
javascripts/db.js: A JavaScript module we’ve created for use by our Node.js apps (in this series, it will be Express) to access MongoDB; this module in turn uses the MongoDB Node.js driver.
config.js: Contains the application–specific configuration options
bin/www: The script that starts an Express application; this is invoked by the npm start script within the package.json file. Starts the HTTP server, pointing it to the app module in app.js
app.js: Defines the main back-end application module (app). Configures:
That the application will be run by Express
Which routes there will be & where they are located in the file system (routes directory)
What view engine to use (Jade in this case)
Where to find the views to be used by the view engine (views directory)
What middleware to use (e.g. to parse the JSON received in requests)
Where the static files (which can be read by the remote client) are located (public directory)
Error handler for queries sent to an undefined route
views: Directory containing the templates that will be used by the Jade view engine to create the HTML for any pages generated by the Express application (for this application, this is just the error page that’s used in cases such as mistyped routes (“404 Page not found”))
routes: Directory containing one JavaScript file for each Express route
routes/pop.js: Contains the Express application for the /pop route; this is the implementation of the Mongopop REST API. This defines methods for all of the supported route paths.
public: Contains all of the static files that must be accessible by a remote client (e.g., our Angular to React apps).
In addition, for the ReactJS client application:
public/react The deployed ReactJS client code; e.g. the JSX code that has been compiled down into vanilla JavaScript
More significant for this post are the new files introduced under the MongoDB-Mongopop-ReactJS folder:
build: Directory containing the compiled and optmized JavaScript (to be copied to the back-end)
node-modules: Node.js modules used by the ReactJS client application (as opposed to the Express, server-side Node.js modules)
public/index.html: Outer template for the application (includes the rootdiv element)
src: Directory JSX source code files we write for the application
index.js: Top-level JSX for the client; creates the <App /> element as a placeholder to be expanded by App.js
App.js: Replaces the <App /> element from index.js with the output from the MongoPopContainer component/class. Includes the rest of the client components
X.component.js: Class implementing sub-component X
data.service.js: Service used to interact with the back-end REST API (mostly used to access the database)
package.json: Instructs the Node.js package manager (npm) what it needs to do; including which dependency packages should be installed
public/index.html defines a div element with its id set to root:
src/index.js accesses the root element from public/index.html so that it can be populated with the output from the application. It imports src/App.js and creates the <App /> element.
src/App.js defines the App class to satisfy the App element in src/index.js; that class renders the <MongoPopContainer /> element, which is made up of all of the sub-components. App.js imports each of the sub-component source files (X.component.js) so that they can implement those components. It also imports src/data.service.js to give access to the back-end Mongopop REST API:
Calling the REST API
The Data Service class hides the communication with the back-end REST API; serving two purposes:
Simplifying all of the components’ code
Shielding the components’ code from any changes in the REST API signature or behavior – that can all be handled within the DataService
The functions of the data service return promises to make working with their asynchronous behaviour simpler. Refer back to Part 2: Using MongoDB With Node.js if you need a recap on using promises.
Apply an update to all documents in a collection
which match a given pattern
This data access class uses the XMLHttpRequest API to make asynchronous HTTP requests to the REST API running in the back-end (mostly to access MongoDB).
One of the simplest functions that data.service.js provides is fetchConfig which sends an HTTP GET request to the back-end to retrieve default the client configuration parameters:
When using this API, the application registers handler functions against a number of possible events; in this case:
onreadystatechange: triggered if/when a successful HTTP response is received
onerror & onabort: triggered when there has been a problem
The method returns a promise which subsequently – via the bound-in function (processRequest & processError) – either:
Provides an object representing the received response
Raises an error with an appropriate message
The baseURL data member is set to http://localhost:3000/pop but that can be changed by editing the data service creation line in App.js:
this.dataService = new DataService("http://localhost:3000/pop");
Another of the methods sends a POST message to the REST API’s pop/addDocs route path to request the bulk addition of documents to a MongoDB collection:
The program flow is very similar to that of the previous function and, in the success case, it eventually resolves the returned promise with a count of the number of documents added.
A final method from the DataService class worth looking at is calculateMongoDBURI() which takes the MongoDB URI provided by MongoDB Atlas and converts it into one that can actually be used to access the database – replacing the <DATABASE> and <PASSWORD> placeholders with the actual values:
The function stores the final URI in the data service class’s MongoDBURI data member – to sent to the back-end when accessing the database (see sendAddDocs above). It also returns a second value (MongoDBURIRedacted) with the password masked out – to be used when displaying the URI.
A simple component that accepts data from its parent
Recall that the application consists of eight components: the top-level application which contains each of the ServerDetails, ConnectionInfo, CollectionName, AddDocuments, CountDocuments, UpdateDocuments, and SampleDocuments components.
When building a new application, you would typically start by designing the the top-level component and then working downwards. As the top-level container is, perhaps, the most complex one to understand, we’ll start at the bottom and then work up.
A simple sub-component to start with is the AddDocuments component:
ReactJS component
A central design decision for any component is what state is required (any variable data that is to be rendered by the component should either be part of the component’s state or of the properties passed by its parent component). The state is initialised in the class’s constructor:
Recall that any state variable X can be read using this.state.X but only the constructor should write to it that way – anywhere else should use the setState() function so that ReactJS is made aware of the change – enabling it to refresh any affected elements. In this class, there are six state variables:
MockarooURL: The URL from a service such as Mockaroo which will return an array containing a set of example JSON documents
numDocsToAdd: How many batches of documents should be added (with the default value of MockarooURL, each batch contains 1,000 documents)
uniqueDocs: Whether each batch should be distinct from the other batches (this significantly slows things down)
numDocsAdded: Updated with the number of added documents in the event that the operation succeeds
errorText: Updated with an error message in the event that the operation fails
addedCollection: Name of the collection that documents were last added to (initialized with the collection property passed by the parent component)
ReactJS state variables
Note that the constructor receives the properties passed down from the parent component. The constructor from the React.Component class must always be invoked within the component’s constructor: super(props).
The binds at the end of the constructor make this available for use within the class’s methods.
Further down in the class is the render() method which returns the content that ReactJS converts to HTML and JavaScript for the browser to render:
Recall that when coding in JSX, JavaScript can be embedded in the HTML by surrounding it with braces. The function uses that almost immediately to include the collection name in the component’s header: <h2>Add documents to {this.props.collection}</h2>.
The first input is initialized with this.state.MockarooURL and if the user changes the value then this.handleURLChange is invoked – which in turn updates the state value:
The same pattern holds for the inputs for numDocsToAdd & uniqueDocs.
When this component’s button is pressed, the onClick event calls this.handleAddSubmit():
This function invokes the sendAddDocs() method of the data service that was passed down from the parent component (and so is part of this.props). sendAddDocs() returns a promise and the first function in the then clause is called if/when that promise is successfully resolved – setting the numDocsAdded state to the number of added documents; if the promise is instead rejected then the second function is called – setting the error message. In either case, the state change will cause the associated element to be rerendered:
Passing data down to a sub-component (and receiving changes back)
The AddDocs component is embedded within the render()method of MongoPopContainer component class; implemented in App.js:
It passes down two items:
dataService is an instance of the DataService class and is used to access the back-end (in particular, to interact with MongoDB). Appears as part of AddDocument‘s properties and can be accessed as this.props.dataService.
collection is a string representing the collection name. Appears as part of AddDocument‘s properties and can be accessed as this.props.collection.
MongoDBCollectionName is initialized, and dataService is instantiated as part of the MongoPopContainer constructor:
Note that for a real, deployed application, http://localhost:3000/pop would be replaced with the public URL for REST API. Additionally, you should consider adding authentication to the API .
But where did the collection name get set – the constructor initialized it to an empty string but that’s not we see when running the application? There’s a clue in the constructor:
CollectionName has a single state variable – collection – which is initially set in the componentDidMount() method by fetching the default client configuration information from the back-end by calling this.props.dataService.fetchConfig(). componentDidMount is one of the component lifecycle methods that are part of any React.Component class – it is invoked after the component has been loaded into the browser, it is where you would typically fetch any data from the back-end that’s needed for the component’s starting state. After setting the collection state, the change notification function passed down by the parent component is invoked to pass up the new value:
_this.props.onChange(_this.state.collection);
Of course, the user needs to be able to change the collection name and so an input element is included. The value of the element is initialized with the collection state variable and when the user changes that value, this.handleCollectionNameChange is invoked. In turn, that method updates the component state and passes the new collection name up to the parent component by calling the change notification method provided by the parent.
Optionally empty components
It’s common that a component should only display its contents if a particular condition is met. Mongopop includes a feature to allow the user to apply a bulk change to a set of documents – selected using a pattern specified by the user. If they don’t know the typical document structure for the collection then it’s unlikely that they’ll make a sensible change. Mongopop forces them to first retrieve a sample of the documents before they’re given the option to make any changes.
This optionality is implemented through the SampleDocuments & UpdateDocuments components:
Flow of data between ReactJS components
Child component
Data passed down
Data changes passed back up
UpdateDocuments
Collection Name
Data service
Sample data to play with
SampleDocuments
Collection Name
Sample data to play with
Data service
Recall that the MongoPopContainer component class includes a state variable named DataToPlayWith which is initialized to FALSE:
That state is updated using the handleDataAvailabiltyChange method:
That method is passed down to the SampleDocuments component:
When the user fetches a sample of the documents from a collection, the SampleDocuments component invokes the change notification method (_this.props.onDataToWorkWith()), passing back TRUE if the request was a success, FALSE otherwise:
MongoPopContainer passes its state variable DataToPlayWith down to the UpdateDocuments component:
Otherwise, the rest of this component is similar to those already seen:
Periodic operations
The CountDocuments component has an extra feature – if the repeat option is checked then it will fetch and display the document count every five seconds. The function that’s called when the count button is clicked, checks the value of the state variable associated with the checkbox and if it’s set, calls setInterval() to call the countOnce() method every five seconds:
The timer is cleared (clearInterval()) if there is an error or just before the component is unmounted (in componentWillUnmount).
Other components
For completeness, this is the full top-level component, App.js, which includes the rest of the sub-components:
Now that the full MERN stack application has been implemented, you can test it from within your browser:
Debugging the ReactJS client is straightforward using the Google Chrome Developer Tools which are built into the Chrome browser. Despite the browser executing the transpiled JavaScript the Dev Tools allows you to navigate and set breakpoints in your JSX code:
Debug React JSX with Google Chrome Developer tools
If there is a compilation error then the error is sent to the browser:
ReactJS Compile errors in Google Chrome Developer tools
So should you use Angular 2 or React for your new web application? A quick Google search will find you some fairly deep comparisons of the two technologies but in summary, Angular 2 is a little more powerful while React is easier for developers to get up to speed with and use (note how many fewer files are needed). The previous blog in this series built the Mongopop client application using Angular 2, while this one built a near-identical app using ReactJS – hopefully these posts have helped you pick a favorite.
The following snapshot from Google Trends suggests that Angular has been much more common for a number of years but that React is gaining ground:
ReactJS popularity vs. Angular and Angular 2
Summary & what’s next in the series
Previous posts stepped through building the Mongopop application back-end and then the creation of an Angular 2 client application. This post described how to build a front-end client using ReactJS. At this point, we have a complete, working, MERN stack application.
The coupling between the front and back-end is loose; the client simply makes remote, HTTP requests to the back-end service – using the interface created in Part 3: Building a REST API with Express.js.
This series will finish by demonstrating alternate methods to implement front-end client applications that aren’t browser-based.
A simpler way to build your app – MongoDB Stitch, Backend as a Service
MongoDB Stitch is a backend as a service (BaaS), giving developers a REST-like API to MongoDB, and composability with other services, backed by a robust system for configuring fine-grained data access controls. Stitch provides native SDKs for JavaScript, iOS, and Android.
Built-in integrations give your application frontend access to your favorite third party services: Twilio, AWS S3, Slack, Mailgun, PubNub, Google, and more. For ultimate flexibility, you can add custom integrations using MongoDB Stitch’s HTTP service.
MongoDB Stitch allows you to compose multi-stage pipelines that orchestrate data across multiple services; where each stage acts on the data before passing its results on to the next.
Unlike other BaaS offerings, MongoDB Stitch works with your existing as well as new MongoDB clusters, giving you access to the full power and scalability of the database. By defining appropriate data access rules, you can selectively expose your existing MongoDB data to other applications through MongoDB Stitch’s API.
This is the first in a series of blog posts examining the technologies that are driving the development of modern web and mobile applications, notably the MERN and MEAN stacks. The series will go on to step through tutorials to build all layers of an application.
Users increasingly demand a far richer experience from web sites – expecting the same level of performance and interactivity they get with native desktop and mobile apps. At the same time, there’s pressure on developers to deliver new applications faster and continually roll-out enhancements, while ensuring that the application is highly available and can be scaled appropriately when needed. Fortunately, there’s a (sometimes bewildering) set of enabling technologies that make all of this possible.
If there’s one thing that ties these technologies together, it’s JavaScript and its successors (ES6, TypeScript, JSX, etc.) together with the JSON data format. The days when the role of JavaScript was limited to adding visual effects like flashing headers or pop-up windows are past. Developers now use JavaScript to implement the front-end experience as well as the application logic and even to access the database. There are two dominant JavaScript web app stacks – MEAN (MongoDB, Express, Angular, Node.js) and MERN (MongoDB, Express, React, Node.js) and so we’ll use those as paths to guide us through the ever-expanding array of tools and frameworks.
This first post serves as a primer for many of these technologies. Subsequent posts in the series take a deep dive into specific topics – working through the end-to-end development of Mongopop – an application to populate a MongoDB database with realistic data and then perform other operations on that data.
The MEAN Stack
We’ll start with MEAN as it’s the more established stack but most of what’s covered here is applicable to MERN (swap Angular with React).
MEAN is a set of Open Source components that together, provide an end-to-end framework for building dynamic web applications; starting from top (code running in the browser) to the bottom (database). The stack is made up of:
Angular (formerly Angular.js, now also known as Angular 2): Front-end web app framework; runs your JavaScript code in the users browser, allowing your application UI to be dynamic
Express (sometimes referred to as Express.js): Back-end web application framework running on top of Node.js
Node.js : JavaScript runtime environment – lets you implement your application back-end in JavaScript
A common theme in the MEAN stack is JavaScript – every line of code you write can be in the same language. You even access the database using MongoDB’s native, IdiomaticJavaScript/Node.js driver. What do we mean by idiomatic? Using the driver feels natural to a JavaScript developer as all interaction is performed using familiar concepts such as JavaScript objects and asynchronous execution using either callback functions or promises (explained later). Here’s an example of inserting an array of 3 JavaScript objects:
Angular, originally created and maintained by Google, runs your JavaScript code within the user’s web browsers to implement a reactive user interface (UI). A reactive UI gives the user immediate feedback as they give their input (in contrast to static web forms where you enter all of your data, hit “Submit” and wait.
Version 1 of Angular was called AngularJS but it was shortened to Angular in Angular 2 after it was completely rewritten in Typescript (a superset of JavaScript) – Typescript is now also the recommended language for Angular apps to use.
You implement your application front-end as a set of components – each of which consists of your JavaScript (TypeScript) code and an HTML template that includes hooks to execute and use the results from your TypeScript functions. Complex application front-ends can be crafted from many simple (optionally nested) components.
Angular application code can also be executed on the back-end server rather than in a browser, or as a native desktop or mobile application.
Express
Express is the web application framework that runs your back-end application (JavaScript) code. Express runs as a module within the Node.js environment.
Express can handle the routing of requests to the right parts of your application (or to different apps running in the same environment).
You can run the app’s full business logic within Express and even generate the final HTML to be rendered by the user’s browser. At the other extreme, Express can be used to simply provide a REST API – giving the front-end app access to the resources it needs e.g., the database.
In this blog series, we will use Express to perform two functions:
Send the front-end application code to the remote browser when the user browses to our app
Provide a REST API that the front-end can access using HTTP network calls, in order to access the database
Node.js
Node.js is a JavaScript runtime environment that runs your back-end application (via Express).
Node.js is based on Google’s V8 JavaScript engine which is used in the Chrome browsers. It also includes a number of modules that provides features essential for implementing web applications – including networking protocols such as HTTP. Third party modules, including the MongoDB driver, can be installed, using the npm tool.
Node.js is an asynchronous, event-driven engine where the application makes a request and then continues working on other useful tasks rather than stalling while it waits for a response. On completion of the requested task, the application is informed of the results via a callback. This enables large numbers of operations to be performed in parallel which is essential when scaling applications. MongoDB was also designed to be used asynchronously and so it works well with Node.js applications.
MongoDB
MongoDB is an open-source, document database that provides persistence for your application data and is designed with both scalability and developer agility in mind. MongoDB bridges the gap between key-value stores, which are fast and scalable, and relational databases, which have rich functionality. Instead of storing data in rows and columns as one would with a relational database, MongoDB stores JSON documents in collections with dynamic schemas.
MongoDB’s document data model makes it easy for you to store and combine data of any structure, without giving up sophisticated validation rules, flexible data access, and rich indexing functionality. You can dynamically modify the schema without downtime – vital for rapidly evolving applications.
It can be scaled within and across geographically distributed data centers, providing high levels of availability and scalability. As your deployments grow, the database scales easily with no downtime, and without changing your application.
MongoDB Atlas is a database as a service for MongoDB, letting you focus on apps instead of ops. With MongoDB Atlas, you only pay for what you use with a convenient hourly billing model. With the click of a button, you can scale up and down when you need to, with no downtime, full security, and high performance.
Our application will access MongoDB via the JavaScript/Node.js driver which we install as a Node.js module.
What’s Done Where?
tl;dr – it’s flexible.
There is clear overlap between the features available in the technologies making up the MEAN stack and it’s important to decide “who does what”.
Perhaps the biggest decision is where the application’s “hard work” will be performed. Both Express and Angular include features to route to pages, run application code, etc. and either can be used to implement the business logic for sophisticated applications. The more traditional approach would be to do it in the back-end in Express. This has several advantages:
Likely to be closer to the database and other resources and so can minimise latency if lots of database calls are made
Sensitive data can be kept within this more secure environment
Application code is hidden from the user, protecting your intellectual property
Powerful servers can be used – increasing performance
However, there’s a growing trend to push more of the functionality to Angular running in the user’s browser. Reasons for this can include:
Use the processing power of your users’ machines; reducing the need for expensive resources to power your back-end. This provides a more scalable architecture, where every new user brings their own computing resources with them.
Better response times (assuming that there aren’t too many trips to the back-end to access the database or other resources)
Progressive Applications. Continue to provide (probably degraded) service when the client application cannot contact the back-end (e.g. when the user has no internet connection). Modern browsers allow the application to store data locally and then sync with the back-end when connectivity is restored.
Perhaps, a more surprising option for running part of the application logic is within the database. MongoDB has a sophisticated aggregation framework which can perform a lot of analytics – often more efficiently than in Express or Angular as all of the required data is local.
Another decision is where to validate any data that the user supplies. Ideally, this would be as close to the user as possible – using Angular to check that a provided password meets security rules allows for instantaneous feedback to the user. That doesn’t mean that there isn’t value in validating data in the back-end as well, and using MongoDB’s document validation functionality can guard against buggy software writing erroneous data.
ReactJS – Rise of the MERN Stack
An alternative to Angular is React (sometimes referred to as ReactJS), a JavaScript library developed by Facebook to build interactive/reactive user interfaces. Like Angular, React breaks the front-end application down into components. Each component can hold its own state and a parent can pass its state down to its child components and those components can pass changes back to the parent through the use of callback functions.
React components are typically implemented using JSX – an extension of JavaScript that allows HTML syntax to be embedded within the code:
React is most commonly executed within the browser but it can also be run on the back-end server within Node.js, or as a mobile app using React Native.
So should you use Angular 2 or React for your new web application? A quick google search will find you some fairly deep comparisons of the two technologies but in summary, Angular 2 is a little more powerful while React is easier for developers to get up to speed with and use. This blog series will build a near-identical web app using first the MEAN and then the MERN stack – hopefully these posts will help you find a favorite.
The following snapshot from Google Trends suggests that Angular has been much more common for a number of years but that React is gaining ground:
Why are these stacks important?
Having a standard application stack makes it much easier and faster to bring in new developers and get them up to speed as there’s a good chance that they’ve used the technology elsewhere. For those new to these technologies, there exist some great resources to get you up and running.
From MongoDB upwards, these technologies share a common aim – look after the critical but repetitive stuff in order to free up developers to work where they can really add value: building your killer app in record time.
These are the technologies that are revolutionising the web, building web-based services that look, feel, and perform just as well as native desktop or mobile applications.
The separation of layers, and especially the REST APIs, has led to the breaking down of application silos. Rather than an application being an isolated entity, it can now interact with multiple services through public APIs:
Register and log into the application using my Twitter account
Identify where I want to have dinner using Google Maps and Foursquare
Order an Uber to get me there
Have Hue turn my lights off and Nest turn my heating down
Check in on Facebook
…
Variety & Constant Evolution
Even when constraining yourself to the JavaScript ecosystem, the ever-expanding array of frameworks, libraries, tools, and languages is both impressive and intimidating at the same time. The great thing is that if you’re looking for some middleware to perform a particular role, then the chances are good that someone has already built it – the hardest part is often figuring out which of the 5 competing technologies is the best fit for you.
To further complicate matters, it’s rare for the introduction of one technology not to drag in others for you to get up to speed on: Node.js brings in npm; Angular 2 brings in Typescript, which brings in tsc; React brings in ES6, which brings in Babel; ….
And of course, none of these technologies are standing still and new versions can require a lot of up-skilling to use – Angular 2 even moved to a different programming language!
The Evolution of JavaScript
The JavaScript language itself hasn’t been immune to change.
Ecma International was formed to standardise the language specification for JavaScript (and similar language forks) to increase portability – the ideal being that any “JavaScript” code can run in any browser or other JavaScript runtime environment.
The most recent, widely supported version is ECMAScript 6 – normally referred to as ES6. ES6 is supported by recent versions of Chrome, Opera, Safari, and Node.js). Some platforms (e.g. Firefox and Microsoft Edge) do not yet support all features of ES6. These are some of the key features added in ES6:
Classes & modules
Promises – a more convenient way to handle completion or failure of synchronous function calls (compared to callbacks)
Arrow functions – a concise syntax for writing function expressions
Generators – functions that can yield to allow others to execute
Iterators
Typed arrays
Typescript is a superset of ES6 (JavaScript); adding static type checking. Angular 2 is written in Typescript and Typescript is the primary language to be used when writing code to run in Angular 2.
Because ES6 and Typescript are not supported in all environments, it is common to transpile the code into an earlier version of JavaScript to make it more portable. In this series’ Angular post, tsc is used to transpile Typescript into JavaScript while the React post uses Babel (via react-script) to transpile our ES6 code.
And of course, JavaScript is augmented by numerous libraries. The Angular 2 post in this series uses Observables from the RxJS reactive libraries which greatly simplify making asynchronous calls to the back-end (a pattern historically referred to as AJAX).
Summary & What’s Next in the Series
This post has introduced some of the technologies used to build modern, reactive, web applications – most notably the MEAN and MERN stacks. If you want to learn exactly how to use these then please continue to follow this blog series which steps through building the MongoPop application:
As already covered in this post, the MERN and MEAN stacks are evolving rapidly and new JavaScript frameworks are being added all of the time. Inevitably, some of the details in this series will become dated but the concepts covered will remain relevant.
A simpler way to build your app – MongoDB Stitch, Backend as a Service
MongoDB Stitch is a backend as a service (BaaS), giving developers a REST-like API to MongoDB, and composability with other services, backed by a robust system for configuring fine-grained data access controls. Stitch provides native SDKs for JavaScript, iOS, and Android.
Built-in integrations give your application frontend access to your favorite third party services: Twilio, AWS S3, Slack, Mailgun, PubNub, Google, and more. For ultimate flexibility, you can add custom integrations using MongoDB Stitch’s HTTP service.
MongoDB Stitch allows you to compose multi-stage pipelines that orchestrate data across multiple services; where each stage acts on the data before passing its results on to the next.
Unlike other BaaS offerings, MongoDB Stitch works with your existing as well as new MongoDB clusters, giving you access to the full power and scalability of the database. By defining appropriate data access rules, you can selectively expose your existing MongoDB data to other applications through MongoDB Stitch’s API.