Copyright Derek O'Reilly, Dundalk Institute of Technology (DkIT), Dundalk, Co. Louth, Ireland.
The MERN stack refers to:
React can be used to write the client-side code for a Node.js web application.
Node.js is a server environment that allows us to write JavaScript code on the server-side. Node.js is a minimal environment.
Express is a web framework that is built on top of Node.js. Express is used to write the server-side code - such as routing, database access and session handling - for a Node.js web application. Express is the most common server-side framework for Node.js.
MongoDB is a database management tool that allows us to build server-side databases. We can use MongoDB with Express.
Nodejs uses a program called nvm (node version manager) to control the version of nodejs that is running. To install nvm on a Windows system:
Installing the above two files will result in a version of npm (node package manager) being installed on your computer. Run the command below to see what version of npm has been installed:
nvm list
In order to be able to use npm, you must first run the command below. You should match the version to the one that was listed by running the "nvm list" command above.
nvm use 24.11.1
In order to run the server-side code of a Node.js app, we need to install a nodemon. The installation of nodemon can be done at the command prompt, using the command below.
npm install -g nodemon
In each section of these notes, we shall download a .zip file that contains a fully working solution. I recomend that you create a folder called c:\nodejs_projects. Each downloaded .zip file can be placed into a folder of the same name.
Create a folder called c:\nodejs_projects\react_app. Copy the code from this link into the folder.
The project consists of a client and a server folder.
To run the server-side of the project you need to run the command prompt nodemon from inside the project's server folder (ie c:\nodejs_projects\react_app\server). Whe nodemon is running, the server-side will automatically restart if any changes are made to the server-side source code.
nodemon
To run the client-side of the project you need to run the command prompt npm start from inside the project's client folder (ie c:\nodejs_projects\react_app\client). When npm start is running, the client-side will automatically restart if any changes are made to the client-side source code.
npm start
IMPORTANT: The server-side command nodemon and client-side command npm start must both be running in two separate command windows for an app to run.
From the above, we can see that a Node.js application requires both a client-side server and a server-side server. The client-side server is responsible for displaying the React Components. The server-side server is responsible for running the node/Express app on the server. When developing our code, we should consider the client-side and server-side code be two related, but totally separate, systems. Understanding that they are two totally separate systems will make it much easier for you to understand and follow these notes.
The full project code for the "Cars" Worked Example that is described below can be downloaded from this link.
Throughout these "Full Stack Development" notes, we shall develop a "Cars" app. Each section of the notes will contain a link to a .zip file, which contains the fully working code relating to the notes in the given section. Each section of the notes will only highlight the changes that have been made in that section of the notes compared to what had been done in previous sections of the notes.
As we can consider the client-side code and server-side code to be two separate systems, each section in the notes will separately describe the Client-Side and Server-Side code, as is done below.
Within the client folder, the index.html file shows that our code is a Progress Web App (PWA). This will allow us to download our app on a desktop or a mobile device. This is covered in detail in the PWA section of these lecture notes. Three other files in the client/public folder (manifest.json, serviceWorker.js and offline.html) all relate to the PWA nature of the app.
client/public/index.html
<!doctype html>
<html lang="en">
<head>
<title>Worked Example</title>
<meta charset="utf-8" />
<link href="/css/offline.css" rel="stylesheet" type="text/css"/>
<link rel="shortcut icon" href="/icons/icon_small.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="white" />
<meta name="description" content="Worked Example" />
<!-- PWA -->
<link rel="manifest" href="/manifest.json" />
<link rel="icon" href="/icons/icon_small.png" type="image/png">
<link rel="apple-touch-icon" href="/icons/icon_medium.png">
<meta name="msapplication-TileImage" content="/icons/icon_large.png">
<meta name="msapplication-TileColor" content="#FFFFFF">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Worked Example">
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
Within the client folder there is a src folder. This is where the React code will be placed.
The file index.js is where the App is rendered. This file also includes code that registers a service worker that makes the app a PWA.
client/src/index.js
import React from "react" import ReactDOM from "react-dom" import App from "./App" ReactDOM.render(<App/>, document.getElementById(`root`)) // Register the service worker window.addEventListener('load', () => { if ("serviceWorker" in navigator) { navigator.serviceWorker.register("/serviceWorker.js") .then(() => console.log("Service Worker Registered")) .catch(err => console.error("Service Worker Registration Failed", err)) } })
The file App.js is the route component of the application. Access to all other client-side react Components is via this file. The App.js file is the base for all applications. Depending on the application, we shall need to add additional code and files. For example, in the App.js file below, we add the DisplayAllCars component:
import React, {Component} from "react"
import "bootstrap/dist/css/bootstrap.css"
import "./css/App.css"
import DisplayAllCars from "./components/DisplayAllCars"
export default class App extends Component
{
render()
{
return (
<DisplayAllCars/>
)
}
}
We need to include any system core Components that we shall use in a particular file.
import React, {Component} from "react"
We need to import any user-defined, project-specific, Components that we shall use in a particular file. For example, below we include the DisplayAllCars component.
import DisplayAllCars from "./components/DisplayAllCars"
We import any core CSS that we shall use in a particular file.
import "bootstrap/dist/css/bootstrap.css"
We need to import any user-defined, project-specific, CSS that we shall use in a particular file.
import "./css/App.css"
Render the DisplayAllCars Component.
export default class App extends Component
{
render()
{
return (
<DisplayAllCars/>
)
}
}
All of the apps' components should be held in a folder called "client/src/components".
import React, {Component} from "react" import CarTable from "./CarTable" export default class DisplayAllCars extends Component { constructor(props) { super(props) this.state = { cars:[] } } componentDidMount() { const cars = [{_id:0, model:"Avensis", colour:"Red", year:2020, price:30000}, {_id:1, model:"Yaris", colour:"Green", year:2010, price:2000}, {_id:2, model:"Corolla", colour:"Red", year:2019, price:20000}, {_id:3, model:"Avensis", colour:"Silver", year:2018, price:20000}, {_id:4, model:"Camry", colour:"White", year:2020, price:50000}] this.setState({cars: cars}) } render() { return ( <div className="form-container"> <div className="table-container"> <CarTable cars={this.state.cars} /> </div> </div> ) } }
We need to include the line of code below in every user-defined Component
import React, {Component} from "react"
Any other core or user-defined Components that are being used by a given Component must be imported into that Component's class file. In this case, the user-defined Component CarTable is imported.
import CarTable from "./CarTable"
Other than the additional code listed above, this and all of the other user-defined Components are coded in exactly the same way as they would be if we were using React in a stand-alone HTML file.
import React, {Component} from "react" import CarTableRow from "./CarTableRow" export default class CarTable extends Component { render() { return ( <table> <thead> <tr> <th>Model</th> <th>Colour</th> <th>Year</th> <th>Price</th> </tr> </thead> <tbody> {this.props.cars.map((car) => <CarTableRow key={car._id} car={car}/>)} </tbody> </table> ) } }
import React, {Component} from "react"
export default class CarTableRow extends Component
{
render()
{
return (
<tr>
<td>{this.props.car.model}</td>
<td>{this.props.car.colour}</td>
<td>{this.props.car.year}</td>
<td>{this.props.car.price}</td>
</tr>
)
}
}
// This file holds global constants that are visible on the Client-side
There are no client-side global constants in this program. We shall use this in later examples.
The server-side code below is the base for all applications. Depending on the application, we shall need to add additional code to this file. The server-side code is shown below:
# This file holds global constants that are visible on the Server-side # Port SERVER_PORT = 4000 # Local Host LOCAL_HOST = http://localhost:3000
The server/config/.env file is hidden from browsers, so it can be used to hold secret keys.
// Server-side global variables
require(`dotenv`).config({path:`./config/.env`})
// Express
const express = require(`express`)
const app = express()
app.use(require(`body-parser`).json())
app.use(require(`cors`)({credentials: true, origin: process.env.LOCAL_HOST}))
// Port
app.listen(process.env.SERVER_PORT, () =>
{
console.log(`Connected to port ` + process.env.SERVER_PORT)
})
// Error 404
app.use((req, res, next) => {next(createError(404))})
// Other errors
app.use(function (err, req, res, next)
{
console.error(err.message)
if (!err.statusCode)
{
err.statusCode = 500
}
res.status(err.statusCode).send(err.message)
})
The code above is the minimum base-code that is needed for all apps. Later in the notes, when we develop more complex examples, we shall add additional code to this file.
In order to access to the environment file variables, we need to import the dotenv package.
dotenv must be installed (using the command line npm install -g dotenv) before we can use it.

We access environment variables using the process.env.variableName. In the code below, we access two environment variables, named LOCAL_HOST and SERVER_PORT. Both of these variables have been declared in the environment file system\config\.env
// Server-side global variables require(`dotenv`).config({path:`./config/.env`}) // Express const express = require(`express`) const app = express() app.use(require(`body-parser`).json()) app.use(require(`cors`)({credentials: true, origin: process.env.LOCAL_HOST})) // Port app.listen(process.env.SERVER_PORT, () => { console.log(`Connected to port ` + process.env.SERVER_PORT) }) // Error 404 app.use((req, res, next) => {next(createError(404))}) // Other errors app.use(function (err, req, res, next) { console.error(err.message) if (!err.statusCode) { err.statusCode = 500 } res.status(err.statusCode).send(err.message) })
In order to use Express, we need to import the Express-related packages. The code below is the minimum required for a basic app. We shall add additional Express-related packages in later examples.
body-parser simplifies incoming requests, as it makes the incoming request body available under req.body property.
cors enables CORS (Cross-Origin Resource Sharing) requests.
// Express const express = require(`express`) const app = express() app.use(require(`body-parser`).json()) app.use(require(`cors`)({credentials: true, origin: process.env.LOCAL_HOST}))
The Port is the URL where the server-side code will run.
// Port
app.listen(process.env.SERVER_PORT, () =>
{
console.log(`Connected to port ` + process.env.SERVER_PORT)
})
The code below deals with server-side error-handling.
// Error 404
app.use((req, res, next) => {next(createError(404))})
// Other errors
app.use(function (err, req, res, next)
{
console.error(err.message)
if (!err.statusCode)
{
err.statusCode = 500
}
res.status(err.statusCode).send(err.message)
})Convert the code from this link into a Node.js app.
Hint 1: Use the code from the React_app above as a starting template
Hint 2: Put the Login component into its own file and replace
class Login extends React.Componentwith
import React, {Component} from "react"
export default class Login extends Component
Hint 3: In App.js, load the Login component instead of the DisplayAllCars component.Convert the code from this link into a Node.js app.
Hint 1: The JSON file needs to be put in a json folder inside the client\public folder
Hint 2: The image file for the modal needs to be put in an images folder inside the client\public folder
Hint 3: The global variable ASCENDING needs to be put inside the client\src\config\global_constants.js, as shown below:
export const ASCENDING = 1Click here for the solution.
Copyright Derek O' Reilly, Dundalk Institute of Technology (DkIT), Dundalk, Co. Louth, Ireland.