React as a node.js App

The full project code for the example code listed on this webpage can be downloaded from this link.

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.

 

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

Create a folder called c:\nodejs_projects\cars_client_side_react. 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\cars_client_side_react\server).


nodemon

Nodemon will automatically restart the server if any changes are made to the server-side source code.

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\cars_client_side_react\client).

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.

Fav Icon

We should replace the client/public/favicon.ico file with our own icon file. We can use a fav icon generator, such as https://realfavicongenerator.net/, to convert an image to an icon. We keep the original filename client/public/favicon.ico and simply overwrite the original icon file with our new icon file.

If we wish to use a different filename for our new icon, then we need to replace favicon.ico with the new icon filename in the two files below:

Client-Side/Server-Side

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.

"Cars" Worked Example

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.

The full project code for the example code listed on this webpage can be downloaded from this link.

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.

Client-Side

Within the client folder of the project that you downloaded there is a src folder. This is where the React code will be placed.

The file App.js is the route component of the application. Access to all other client-side react Components is via this file.

 

The client-side code below is the base for all applications. Depending on the application, we shall need to add additional code and files. The client-side code is shown below:

client/src/App.js

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 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"

We need to import any user-defined, project-specific, Components that we shall use in a particular file.

import DisplayAllCars from "./components/DisplayAllCars"

Render the DisplayAllCars Component.

export default class App extends Component 
{
    render() 
    {
        return (
            <DisplayAllCars/>
        )
    }
}

The three files listed below are generated by the syste. They do not need to change between applications:

 

 

All of the apps' components should be held in a folder called "client/src/components".

client/src/components/DisplayAllcars.js

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.

client/src/components/CarTable

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>      
        )
    }
}

 

client/src/components/CarTableRow.js

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>
        )
    }
}

 

client/config/global_constants.js

// 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.

client/public/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Example MERN app"
    />
    <link rel="apple-touch-icon" href="logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>Example MERN app</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

We only need to change the title of the app in this system-generated file.

client/public/manifest.json

{
  "short_name": "Example MERN App",
  "name": "Example MERN App",
  "icons": [
    {
      "src": "favicon.ico",
      "sizes": "64x64 32x32 24x24 16x16",
      "type": "image/x-icon"
    }	    
  ],
  "start_url": ".",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff"
}

We only need to change the title of the app in this system-generated file.

\client\package.json

{
  "name": "jwt",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "axios": "^0.19.0",
    "bootstrap": "^4.3.1",
    "react": "^16.9.0",
    "react-bootstrap": "^1.0.0-beta.11",
    "react-dom": "^16.9.0",
    "react-router-dom": "^5.0.1",
    "react-scripts": "3.1.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "description": "Example using JSON Web Tokens",
  "main": "index.js",
  "devDependencies": {},
  "author": "Derek O Reilly",
  "license": "ISC"
}

We only need to change the description of the app and the app's author in this system-generated file.

Server-Side

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:

server/config/.env

# This file holds global constants that are visible on the Server-side


# Port
SERVER_PORT = 4000


# Local Host
LOCAL_HOST = http://localhost:3000
Environment variables are global to all files on the server-side.

The server/config/.env file is hidden from browsers, so it can be used to hold secret keys.

server/server.js

// 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 -g install 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.

Convert the code from this link into a Node.js app.

 
<div align="center"><a href="../versionC/index.html" title="DKIT Lecture notes homepage for Derek O&#39; Reilly, Dundalk Institute of Technology (DKIT), Dundalk, County Louth, Ireland. Copyright Derek O&#39; Reilly, DKIT." target="_parent" style='font-size:0;color:white;background-color:white'>&nbsp;</a></div>