Skip to main content

MERN-Stack Infrastructure - Part 1

Learning Objectives

Students Will Be Able To:
Organize the React Frontend and Express Server Code
Implement Environment Variables Using the dotenv Module
Connect to a MongoDB Using Mongoose

Road Map

  1. Setup
  2. The 7-Part Plan for mern-infrastructure
  3. Tidy Up the React App Generated by create-react-app (CRA)
  4. Add Folders Used to Organize React Components
  5. Add Folders to Organize the Express Server Code
  6. Install and use dotenv to process a .env file
  7. Install mongoose and Connect to a Database
  8. Add an "external" crud-helper.js Module Used for Testing Models, etc.

Videos

Video 📹 Link

1. Setup

This lesson continues to build-out the mern-infrastructure project right where the Intro to Single-Page Applications (SPAs) and the MERN-Stack lesson left off.

Move into your ~/code folder:

cd ~/code

Open the project in VS Code:

code .

Start the Express Backend

Open an integrated Terminal in VS code (control + backtick).

Start the Express server:

nodemon server

Start the React Development Server

Open a second integrated Terminal in VS code (control + backtick).

Start the React Development server:

npm run start

👀 Do you need to sync your code?


git reset --hard origin/sync-2-part-1-starter


2. The 7-Part Plan for mern-infrastructure

Over the next several lessons, we'll continue building out mern-infrastructure by implementing the boilerplate that's a part of any MERN-Stack app of significance.

Along the way, we will sprinkle in just a tad of SEI CAFE specific content when it makes sense to do so.

Here's the Plan:

Part 1 - Project Setup:

  1. Tidy up the React app generated by create-react-app (CRA)
  2. Add folders used to organize React components
  3. Add folders to organize the Express server code
  4. Install and use dotenv to process a .env file
  5. Install mongoose and connect to a database
  6. Add an "external" crud-helper.js module used for testing models, etc.

Part 2 - Client-Side Routing:

  1. Set up user state
  2. Add skeleton page-level components
  3. Conditionally render based on the user state
  4. Intro to client-side routing using React Router
  5. Implement client-side routing
  6. Implement a typical navigation bar

Part 3 - Implement Token-Based Auth:

  1. The process of adding a feature to a MERN-Stack app
  2. Code the <SignUpForm> component as a class component
  3. Add service & API modules on the client

Part 4 - Implement Token-Based Auth (continued):

  1. Review of fetch
  2. Review of handling promises with async/await
  3. Make the AJAX request to sign-up
  4. Define the server-side route for signing-up
  5. Define the controllers/api/users.js module
  6. Mock the create (sign-up) controller action

Part 5 - Implement Token-Based Auth (continued):

  1. Discuss token-based authentication
  2. Add the User model
  3. Implement the create (sign-up) controller action

Part 6 - Implement Token-Based Auth (continued):

  1. Save the token in the browser's local storage
  2. Update the user state
  3. Implement logging out
  4. Implement logging in

Part 7 - Implement Token-Based Auth (continued):

  1. Send the token with AJAX requests
  2. Check the token on the server and add a user property to req
  3. Implement middleware to protect server-side routes
  4. Save MERN-Stack infrastructure to a new GH repo

Then, once all of the above infrastructure is in place, we'll save it to a separate GitHub repo for the purpose of being able to start new MERN-Stack projects by simply cloning it at anytime in the future!

3. Tidy Up the React App Generated by create-react-app (CRA)

Let's tidy up the generated React app by deleting some files we don't need at this time.

❓ Which JS file is the entry point of the React app?

src/index.js

src/index.js

We're not going to be concerned with reporting the performance of this app, so we can optionally remove the reportWebVitals() call and the import. Feel free to leave it if you wish and read the link for more info when you have time.

While in index.js, take note that it imports index.css which holds our application-wide styles.

Also as we learned, this is where the top-level <App> component is rendered from. Let's tidy up App.js now...

src/App.js

For better Emmet support and to stay consistent with the way we will name our future components, let's rename App.js to App.jsx.

note

It may be necessary to restart React's Dev Server to get the app to compile.

Now in App.jsx we're going to:

  • Change the outer <div> to be a <main> React Element.
  • Clear out all of the <App> components children.
  • Add "App" as placeholder text.
  • Remove the import of logo.
  • Move the export default in front of the function
App.jsx
import "./App.css";

export default function App() {
return <main className="App">App</main>;
}
note

Note that import React from 'react'; is no longer necessary with the latest version of the library's JSX transform and build configuration.

Now we can remove the following unused files from the project:

  • App.test.js
  • logo.svg
  • reportWebVitals.js (assuming you removed it from index.js)
  • setupTests.js

Also, if you removed reportWebVitals.js, you can uninstall it from the package.json by running npm uninstall web-vitals.

public/index.html

Let's not forget to update the <title> element in the <head> of index.html.

You'll find index.html in the public folder, along with a few other static assets such as favicon.ico.

Let's update the <title> element as follows:

index.html
<title>MERN Infrastructure</title>

4. Add Folders Used to Organize React Components

A typical React app has a high number of modules and we'll want to create a folder structure that best organizes these modules.

Dedicated Component Folders

In React apps, it's a best practice to create a dedicated folder for each component.

Yes, this will result in quite a few folders, however, real-world React components often have dedicated CSS and test modules in addition to the module for the component itself - so it makes sense to keep these modules together in their own folder.

Let's create a folder for our only component at this time:

mkdir src/App
info

The folder should be named exactly as that of the component - without the file extension, of course.

Now let's move the App related modules into the new App folder:

mv src/App.* src/App

The above move will require us to fix the import within index.js, which we'll do a bit later because we're not done organizing yet...

Folders for Organizing Page-Level and Non-Page Components

Because of the high number of components in a typical React app, we'll create two additional folders to use as a further level of organization:

  • src/pages: This folder will hold the "page-level" components that implement the app's main functionality. Page-level components are rendered for each client-side route. For example, in the past where we might have rendered a movies/show.ejs template for a GET /movies/:id route, we now might want to render a MovieDetailPage.jsx component when at the /movies/:id client-side route instead. We'll move the <App> component into the src/pages folder as well.

  • src/components: This folder will hold all other non-page-level components. These components may often be used/rendered by any number of other components.

Let's create the above two folders:

mkdir src/components src/pages

Now let's move the <App> component's folder into src/pages:

mv src/App src/pages

Update the Import in index.js

The previous restructuring requires an update to the way App.jsx is being imported within index.js:

index.js
// index.js

import App from "./pages/App/App";
note

Note: Again, it may be necessary to restart React's Dev Server to get the app to compile.

5. Add Folders to Organize the Express Server Code

While we're creating folders used to organize our code, let's create some for the Express app that you're familiar with:

mkdir config routes models controllers

We'll be using the above folders to organize our server-side code soon enough.

6. Install and use dotenv to process a .env file

As we've seen in the past, we need a way to access environment variables and secrets.

We're going to use the dotenv module to read the key=value pairs in a .env file exactly like we did in Unit 2.

Install and Run the dotenv Module

First the install:

npm i dotenv

Now let's add it to server.js:

server.js
const logger = require("morgan");

// Always require and configure near the top
require("dotenv").config();

Create the .env File

Be sure to touch the .env file in the project root folder:

touch .env

Now we're setup to hold secrets such as the database connection string...

7. Install mongoose and Connect to a Database

As you'll remember, in Unit 2 we used the Mongoose library to interact with a MongoDB database.

Mongoose is also the go to in MERN-Stack apps.

First, we need to install it as usual:

npm i mongoose

Create the config/database.js Module

Just like we did in Unit 2, we'll use a dedicated Node module to connect to the database:

touch config/database.js

This code might look familiar:

config/database.js
const mongoose = require("mongoose");

mongoose.connect(process.env.DATABASE_URL);

const db = mongoose.connection;

db.on("connected", function () {
console.log(`Connected to ${db.name} at ${db.host}:${db.port}`);
});

Add the DATABASE_URL to the .env

Go copy your Atlas connection string (DATABASE_URL) from the .env file of your Project 2 or mongoose-movies and paste it into the .env we created a moment ago:

DATABASE_URL=mongodb+srv://seistudent:thepassword@cluster0.alq0ver.mongodb.net/mongoose-movies?retryWrites=true&w=majority

Now update the the database name from whatever it currently is, e.g., mongoose-movies to something like mern-infrastructure.

DATABASE_URL=mongodb+srv://seistudent:thepassword@cluster0.alq0ver.mongodb.net/mern-infrastructure?retryWrites=true&w=majority

Connect to the Database

We require database.js in server.js in order to connect to the database:

server.js
require("dotenv").config();

// Connect to the database
require("./config/database");
caution

Be sure to require the config/database module after dotenv.

Looking good:

8. Add an "external" crud-helper.js Module Used for Testing Models, etc

We've previously used a Node REPL to perform CRUD and test our models by following these instructions.

It's worth the short amount of time it takes to create a module we can load in a Node REPL any time we need to perform CRUD outside of our application.

The module is not run as part of the Express app - it's "external" to it.

Start by creating the module:

touch crud-helper.js

There's no models to require at this time, but we'll add them as we build out SEI CAFE:

crud-helper.js
// Connect to the database
require("dotenv").config();
require("./config/database");

// Require the Mongoose models
// const User = require('./models/user');
// const Item = require('./models/item');
// const Category = require('./models/category');
// const Order = require('./models/order');

// Local variables will come in handy for holding retrieved documents
let user, item, category, order;
let users, items, categories, orders;

This is how we will use the crud-helper.js module in the future:

mern-infrastructure[main*] % node
Welcome to Node.js v18.11.0.
Type ".help" for more information.
> .load crud-helper.js
// Connect to the database
require('dotenv').config();
require('./config/database');

// Require the Mongoose models
// const User = require('./models/user');
// const Item = require('./models/item');
// const Category = require('./models/category');
// const Order = require('./models/order');

// Local variables will come in handy
let user, item, category, order;
let users, items, categories, orders;

{}
> Connected to mern-infrastructure at localhost:27017

Type .exit or control + c twice to exit the REPL.

It sure doesn't look like much yet - but it's a start!

Congrats - we're off and running toward the MERN-Stack!

👀 Do you need to sync your code?


git reset --hard origin/sync-2-part-1-finish