- Contributions
- TLDR
- Overview
- Resources
- Getting Started
- Backend Setup (Node.js/Express)
- Frontend Setup (Vite)
- React and Node.js Assignment - Connecting to a MySQL Database
- Understanding Terminal Commands and NPM Scripts
- Build and Deploy
- Screenshots of Build and API Served With Forever
This guide was developed by Devin Daniels and Zachary Maes under the supervision of Dr. Michael Curry and Dr. Danielle Safonte.
Assuming you are ssh logged into flip or classwork osu servers. To view the application you must be logged into the osu vpn service.
-
Clone the starter app repository:
git clone [email protected]:osu-cs340-ecampus/react-starter-app.git
- Follow the git guide on canvas, you may need a github personal access token
-
Navigate to the
/App
directory:cd react-starter-app/App
-
Set up the backend:
- Change directory to /backend/database:
cd ../backend/database
- Login to MariaDB in terminal
- In Mariadb run
source ddl.sql;
, and confirm the database is added without error. Then exit Mariadb. - Change directory to /backend:
cd ..
- Update all of your .env information locally, see Backend Setup (Node.js/Express)
- Install necessary packages:
npm install
- Launch the backend:
npm start
- Change directory to /backend/database:
-
Set up the frontend:
- Change directory to frontend:
cd frontend
- Update all of your .env information locally, see Frontend Setup (Vite)
- Install necessary packages:
npm install
- Launch the frontend:
npm start
- Change directory to frontend:
-
Begin your development journey: Happy Hacking!
This guide is tailored for students enrolled in CS 340 who aim to develop their final project using React.js, Node/Express, and MySQL. The goal of this repo is to provide you with the basic structure of a React/Vite + Express/Node full-stack application, including a few example SQL queries.
The starter code provided in this guide encompasses essential components such as tool setup, infrastructure for building and running your application, and guidelines for deploying your application to OSU's Flip server.
Key Assumptions:
- You have read through and understand the nodejs-starter-app
- That guide uses nodejs, express, and handlebars, but goes deeper into the inner workings of express and nodejs.
- You have a foundational understanding of JavaScript and MySQL syntax.
- You are adept at using terminal commands like
cd
,ls
,mkdir
, etc. - Access to OSU's flip servers and a MySQL database is available to you.
- Note: Adaptations for local development are possible and outlined in this guide.
- You have some familiarity with react hooks like
useState()
anduseEffect()
.- The code in this guide uses these hooks for state management and network requests. Examples of these are provided and described, but you may need to do more research for a better understanding of how they work.
-
Videos - CRUD in React
IDE: Visual Studio Code
Browser: Chrome or Firefox are recommended, though other browsers will probably suffice.
VScode SSH extension: Visual Studio Code SSH Documentation
Terminal: The built-in terminal in VSCode works great.
Flip Server: flip 1-4 or classwork servers and ports associated with those [1024 < PORT < 65535]
OSU VPN: Access to vpn services (see canvas) required for viewing links on flip.
-
Create a
.env
file in the root directory (.env
should have the same indentation asserver.js
, both need to be at the root of the directory). -
Git cloning does not include the
.env
file for obvious reasons. Here is the naming schema I would suggest (fill in with info from activity 2 - Connecting to 340 DB)):DB_HOST="classmysql.engr.oregonstate.edu" # keep this DB_USER="cs340_youronid" # replace with your onid DB_DATABASE="cs340_youronid" # replace with your onid DB_PASSWORD="****" # your db password - last 4 digits of osu id number PORT=8500 # Set a port number between:[1024 < PORT < 65535]
-
server.js
is the entry point for the backend. No changes are needed here except perhaps updating theconsole.log()
statement in theapp.listen()
to reflect the FLIP server you've connected to via SSH. -
Inside your flip server you will need to set up the
ddl.sql
file located inside/backend/databases/
using the source command.# change directory to where the ddl.sql file is located cd react-starter-app/App/backend/databases # or wherever the ddl.sql is located # normal login command (if not using shortcut) mysql -u cs340_youronid -h classmysql.engr.oregonstate.edu -p cs340_youronid # type in your 4 digit mariadb password and press 'enter' key
flip will login to mariadb...
Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 1359790 Server version: 10.6.16-MariaDB-log MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
Use 'show tables;' command to ensure that your database is empty, backup tables if necessary prior to moving on, drop all of your tables.
MariaDB [cs340_maesz]> SHOW tables; Empty set (0.001 sec)
Now you can source the ddl.sql
MariaDB [cs340_maesz]> source ddl.sql; Query OK, 0 rows affected (0.001 sec) Query OK, 0 rows affected, 1 warning (0.004 sec) Query OK, 0 rows affected (0.007 sec) Query OK, 11 rows affected (0.001 sec) Records: 11 Duplicates: 0 Warnings: 0 Query OK, 0 rows affected, 1 warning (0.003 sec) Query OK, 0 rows affected (0.005 sec) Query OK, 4 rows affected (0.000 sec) Records: 4 Duplicates: 0 Warnings: 0 Query OK, 0 rows affected, 1 warning (0.003 sec) Query OK, 0 rows affected (0.006 sec) Query OK, 9 rows affected (0.001 sec) Records: 9 Duplicates: 0 Warnings: 0 Query OK, 0 rows affected (0.007 sec) Query OK, 8 rows affected (0.001 sec) Records: 8 Duplicates: 0 Warnings: 0 Query OK, 0 rows affected (0.000 sec)
Confirm everything sourced with a
show tables;
commandMariaDB [cs340_maesz]> show tables; +-----------------------+ | Tables_in_cs340_maesz | +-----------------------+ | bsg_cert | | bsg_cert_people | | bsg_people | | bsg_planets | +-----------------------+ 4 rows in set (0.001 sec)
Exit mariadb
MariaDB [cs340_maesz]> exit Bye flip3 ~/react-starter-app/App/backend/database 1010$ # We are now back in the terminal
-
Now you must install all the node dependencies outlined in the
package.json
andpackage-lock.json
. Run the following commands to do this:Change directory to
/backend
, wherever that exists in your file structure.cd ~/react-starter-app/App/backend
Use
npm install
to download everything (Some of you may have to debug this step if anything goes wrong...):flip3 ~/react-starter-app/App/backend 1023$ npm install # installer does some magic... added 122 packages, and audited 123 packages in 28s 15 packages are looking for funding run `npm fund` for details found 0 vulnerabilities flip3 ~/react-starter-app/App/backend 1024$ ▌
-
Now you can start your application with the start script located in the
package.json
flip3 ~/react-starter-app/App/backend 1024$ npm start > [email protected] start > nodemon server.js [nodemon] 3.0.2 [nodemon] to restart at any time, enter `rs` [nodemon] watching path(s): *.* [nodemon] watching extensions: js,mjs,cjs,json [nodemon] starting `node server.js` Server running: http://flip3.engr.oregonstate.edu:8500... ▌
This repo uses the package
nodemon
to run your program continuously. You may also install the packageforever
to accomplish this, see the nodejs-starter-app for instructions regarding theforever
package.Remember, you must change the port numbers!
This section will guide you through setting up the frontend part of your application using Vite and React. Create React App has traditionally been the go to way to develop a react project, but It has been deprecated (no longer being updated or has support). Vite is the more modern bundling solution, which is what we use in this project. It is highly recomended that you read the documentation for Vite to understand how it works. It is very similar to CRA so many tutorials for CRA are still appicable to Vite projects for when you are searching the web for help.
Vite allows you to write react code, start a development server with npm start
, and build a static /dist
folder to serve your finished application (more on this in a later section). The scripts for these can be found or modified inside the package.json
. Now we will walk through how to get your development server set up and running.
-
Navigate to the
/frontend
directory.flip3 ~/react-starter-app/App 1006$ cd frontend
-
Modify the fronted
.env
file so thatVITE_API_URL
matches the backend api url you created in the prior steps. Also modify the frontendVITE_PORT
for your dev server to run on. We will cover the# Vite environment variables # Note: Every team member should have their own version of this file locally. Do not commit this to github # in real world development scenarios, this may contain sensitve keys, that is why this is listed on .gitignore # PLEASE CHANGE THE PORTS TO YOUR LIKING ... between:[1024 < PORT < 65535] # Remember: only one person can use a port at a time # This is the port used for your vite development server with `npm start` or `npm run dev`. That is configured in the the file vite.config.js # When you run `npm start` or `npm run dev` you can access your website on port 8501. Change this to your liking. VITE_PORT=8501 # Set a port number between:[1024 < PORT < 65535], this should NOT be the same as the API port. # This is the port used in the /frontend/reactServer.js to host your '/dist' build folder after running 'npm build', change this to your liking. REACT_SERVER_PORT=6061 # This is the port used in the /frontend/reactServer.js to host your '/dist' build folder... more on this later in the guide... # This is the url that points to your 'backend/server.js' and you must change the flip number and port to match what you set up there. # This url is where your SQL server code recieves and sends CRUD operations. VITE_API_URL='http://flip3.engr.oregonstate.edu:8500/api/' # Change this url to match your backend express api url and port.
The
VITE_API_URL
environment variable is used to fetch data from the backend api to this frontend application with axios in components likePersonTable.jsx
. Here is a function from that file to demonstrate this:// FILE: PersonTable.jsx const fetchPeople = async () => { try { const URL = import.meta.env.VITE_API_URL + "people"; const response = await axios.get(URL); setPeople(response.data); } catch (error) { alert("Error fetching people from the server."); console.error("Error fetching people:", error); } };
The
VITE_PORT
environment variable is used to modify the frontend port that this vite application runs on. This is set up inside the filevite.config.js
. Usually the default port for vite react projects is5173
, but we are using dotenv to modify this to a port of your choosing. You can see how this works below:// FILE: vite.config.js import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import dotenv from 'dotenv' dotenv.config() // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], esbuild: { loader: "jsx" }, server: { // Use VITE_PORT from your .env, or default to a port if not specified port: parseInt(process.env.VITE_PORT, 10) || 5173 } })
-
Install all the frontend dependencies. This will add the
node_modules
folder so that your project can run properly.flip3 ~/react-starter-app/App/frontend 1008$ npm install # installer does some magic... added 284 packages, and audited 285 packages in 17s 99 packages are looking for funding run `npm fund` for details found 0 vulnerabilities flip3 ~/react-starter-app/App/frontend 1008$ ▌
-
Now you are ready to start the application using the start script inside the
package.json
. When working with the Vite development server on a remote server (e.g., flip3), there are different ways to start the server and access your application, depending on whether you need local access (on the remote server itself) or external access (from your own computer or the internet).
To accomplish these options, this guide assumes that you are following the instructions for SSH through Vscode using the Remote - SSH Vscode plugin. The instructions for setting up your ssh client can be found on the Canvas Practice Resources.
With that being said, this magical VSCode plugin is able to auto-forward the ports from the remote flip server to your local machine. This means that when you start the development server on flip, VSCode can tunnel the server's port back to your local machine, so you can access it as if it were running locally via localhost:PORT/
or 127.0.0.1:PORT/
. Please note that to view the URLs in both options below, you must be signed into the VPN.
When you start the Vite server using the standard npm run start
command, the server binds to localhost
of the remote machine. The VSCode Remote - SSH plugin automatically detects this and sets up port forwarding, allowing you to access the server using localhost
or 127.0.0.1
in your local machine's browser. This method keeps the server private to your local machine, and it cannot be viewed by anyone else except you.
flip3 ~/react-starter-app/App/frontend 1003$ npm run start
> [email protected] start
> vite
VITE v5.1.4 ready in 525 ms
➜ Local: http://127.0.0.1:8501/ or http://localhost:8501/
➜ Network: use --host to expose
➜ press h + enter to show help
h # use h to see some options
Shortcuts
press r + enter to restart the server
press u + enter to show server url
press o + enter to open in browser
press c + enter to clear console
press q + enter to quit
q # Use q to stop vite from running and return to the terminal.
flip3 ~/ula_cs340/winter24/react-starter-app/App/frontend 1003$ ▌
If you need to share your development server with teammates or access it from devices other than your local machine, you can start the server with the --host
option by running npm start -- --host
. This tells Vite to listen on all network interfaces, making the server accessible via the remote server's public IP address. Note that VSCode will still forward this port, but now other devices can also access the server if they can reach the remote server's IP.
flip3 ~/ula_cs340/winter24/react-starter-app/App/frontend 1003$ npm start -- --host
> [email protected] start
> vite "--host"
VITE v5.1.4 ready in 1330 ms
➜ Local: http://localhost:8501/ or http://127.0.0.1:8501/
➜ Network: http://128.193.36.41:8501/ or http://flip3.engr.oregonstate.edu:8501/
# Now anyone with the VPN can view your dev server at these network URLs
➜ press h + enter to show help
h # use h to see some options
Shortcuts
press r + enter to restart the server
press u + enter to show server url
press o + enter to open in browser
press c + enter to clear console
press q + enter to quit
q # Use q to stop vite from running and return to the terminal.
flip3 ~/ula_cs340/winter24/react-starter-app/App/frontend 1004$ ▌
While testing the command npm start -- --host
, it was observed that using ^C
to send the SIGINT
signal does not always lead to a clean shutdown of the Vite development server. This is because SIGINT
may not terminate child processes spawned by Vite, leading to the server process lingering in the background.
If you encounter issues where the server's port remains in use even after attempting to stop the server with ^C
, you can follow these steps for a more forceful shutdown:
-
Identify the lingering process:
- Use
lsof -i :<YOUR PORT>
to list all processes using the port. - Alternatively,
ps -u $(whoami)
lists all your running processes, helping identify the PID of the node process for Vite.
- Use
-
Terminate the process:
- Use
kill -9 <PID>
to forcefully terminate the identified process. Replace<PID>
with the actual process ID you found in the previous step.
- Use
Note: Always attempt to gracefully shut down the server using the built-in quit command (if available) or the standard ^C
method first. Resort to kill -9
only when necessary as it terminates processes abruptly, without allowing them to clean up resources or perform a graceful shutdown. I discovered this when I exited out of my ssh session and I was still able to access my application running on http://flip3.engr.oregonstate.edu:8501/
even though I had used ^C
. When I logged back into flip via ssh, upon trying to run npm start -- --host
or npm start
Vite would automatically tell me that port 8501
was still in use, and then it would start a new server on port 8502
.
# use this command to see what the PID of the process is that is running on a specific port.
flip3 ~ 1009$ lsof -i :8501
# Our runaway process is identified as 2502508
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 2502508 maesz 22u IPv6 357398260 0t0 TCP *:cmtp-mgt (LISTEN)
node 2502508 maesz 29u IPv6 357423874 0t0 TCP flip3.engr.oregonstate.edu:cmtp-mgt->10-197-151-117.sds.oregonstate.edu:64081 (ESTABLISHED)
node 2502508 maesz 30u IPv6 357944796 0t0 TCP flip3.engr.oregonstate.edu:cmtp-mgt->10-197-151-117.sds.oregonstate.edu:64620 (ESTABLISHED)
# use this command to see all of your running processes
flip3 ~ 1010$ ps -u $(whoami)
PID TTY TIME CMD
2501520 ? 00:00:00 systemd
2501523 ? 00:00:00 (sd-pam)
2501554 ? 00:00:00 sshd
2501556 pts/393 00:00:00 bash
2502508 pts/393 00:00:03 node # This is our runaway process 2502508
2502541 pts/393 00:00:01 esbuild
2512357 ? 00:00:00 sh
2512428 ? 00:00:09 node
2513575 ? 00:00:04 node
2517189 ? 00:00:00 sshd
2517190 ? 00:00:00 bash
2517570 ? 00:00:00 bash
2517908 ? 00:00:18 node
2517920 ? 00:00:00 node
2518045 ? 00:00:04 node
2518267 ? 00:00:00 sshd
2518272 ? 00:00:00 bash
2518654 ? 00:00:00 bash
2518673 ? 00:00:00 code-903b1e9d89
2518719 ? 00:00:00 sh
2518721 pts/464 00:00:00 bash
2552243 pts/525 00:00:00 bash
2556916 ? 00:00:01 node
2574369 ? 00:00:00 sshd
2574370 pts/455 00:00:00 bash
2614401 ? 00:00:00 sleep
2615639 pts/455 00:00:00 ps
# use this command to kill the process with the PID
flip3 ~ 1011$ kill -9 2502508
Now that your application is set up and running, this section will guide you through adding a diagnostic API endpoint to your Node.js Express backend and displaying its data on your React application's homepage. This integration will ensure your backend and database are correctly configured and communicating with your frontend.
First, ensure you have MySQL installed and set up in your environment. Then, install the MySQL package in your /backend
directory if you haven't already with npm install mysql --save
.
In your /backend/server.js
file, import your database connector and define a new GET
request route at /api/diagnostic
that performs a series of database operations:
// Match to your database config route
const db = require('./database/config.js');
// define a new GET request with express:
app.get('/api/diagnostic', async (req, res) => {
try {
// Await your database queries here
await db.pool.query('DROP TABLE IF EXISTS diagnostic;');
await db.pool.query('CREATE TABLE diagnostic(id INT PRIMARY KEY AUTO_INCREMENT, text VARCHAR(255) NOT NULL);');
await db.pool.query('INSERT INTO diagnostic (text) VALUES ("MySQL is working!")');
const results = await db.pool.query('SELECT * FROM diagnostic;');
// res.json() automatically stringifies the JavaScript object to JSON
res.json(results);
} catch (error) {
// Handle Errors
console.error('Database operation failed:', error);
res.status(500).send('Server error');
}
});
Update the HomePage Component by modifying /frontend/HomePage.jsx
to fetch and display data from the endpoint you created at /api/diagnostic
.
import { useState, useEffect } from 'react'; // import the hooks you are going to use
import axios from 'axios';
// Define the HomePage component
function HomePage() {
// useState hook to initialize the diagnosticData state variable to store the fetched data
const [diagnosticData, setDiagnosticData] = useState([]);
// Define a function to fetch diagnostic data from the API
const fetchDiagnosticData = async () => {
try {
// Construct the URL for the API call
const URL = import.meta.env.VITE_API_URL + 'diagnostic';
// Use Axios to make the GET request
const response = await axios.get(URL);
// Update state with the response data
setDiagnosticData(response.data);
} catch (error) {
// Handle any errors that occur during the fetch operation
console.error('Error fetching diagnostic data:', error);
alert('Error fetching diagnostic data from the server.');
}
};
// useEffect hook to trigger the fetchDiagnosticData function when the component mounts
useEffect(() => {
fetchDiagnosticData();
}, []);
// Determine content based on diagnosticData length from the fetch action
let content;
if (diagnosticData === null) {
content = <p>Loading diagnostic data...</p>; // Show while data is null
} else if (diagnosticData.length === 0) {
content = <p>No diagnostic data found.</p>; // Show if data is an empty array
} else {
content = <pre>{JSON.stringify(diagnosticData, null, 2)}</pre>;
}
// display the content and anything else
return (
<>
<h2>Diagnostic Data</h2>
{content}
<h2>Feel free to add any information you like about your project</h2>
</>
);
}
export default HomePage;
Now that you have set up the express backend route and frontend Homepage fetch, you should now be able to see this when you npm run start
the dev servers for both backend and frontend.
- Start your backend server and ensure it's running without errors.
- Start your React frontend and navigate to the homepage.
- You should see the diagnostic data displayed on the page, confirming that the backend and frontend are correctly integrated.
- In the browser dev tools network tab you should see the fetch to
/api/diagnostic
with its request and response. - You should see any console.log() statements you added in the backend code in the terminal on flip where the server.js is running.
By following these steps, you've successfully added a diagnostic API endpoint to your backend and displayed its response in your React frontend. This setup is a foundational step towards building full-stack web applications that require backend and frontend integration. Once you are satisfied that the frontend application is communicating with the backend server.js in the npm run start
defined dev servers, you will need to take steps to build and serve a working URL that other people on the OSU VPN may observe the functionality of the diagnostic fetch. Any URLs you created with npm run start
do not persist once you log out of the ssh session on the flip servers. The link you submit to canvas will need to stay live and functional for TAs and Instructors to complete their grading. You may choose to modify the configurations of this repo to fit your specific needs for using various OSU servers (flip1, flip2, classwork, etc...). Please refer to the remaining sections below to learn how to build and deploy your react application. The section Build and Deploy should help you get started on that.
The package.json
files in both the /frontend
and /backend
directories of our project serve as a manifest for project settings, dependencies, and, importantly, scripts that automate tasks. These scripts are custom commands defined under the "scripts"
property and can be executed using npm or npx, simplifying the development and deployment processes. In this section we will iteratively learn about the various ways you can start up a project server, and how these can be traslated into efficient npm scripts.
To set a foundation for why npm scripts make development more efficient, let's first look at how you would start up the backend/server.js
the traditional way using the command node server.js
:
flip3 ~/react-starter-app/App/backend 1025$ node server.js
Server running: http://flip3.engr.oregonstate.edu:65432...
▌
You will notice that this command takes over your terminal until you send the control + C
| ^C
| SIGINT
command. Another disadvantage of using node server.js
is that when you make changes to the code, you must stop and restart the process before you can see those changes.
Now you might be saying to yourself, "Can't I just use nodemeon
to solve this?"... YES!
Let's now look at how you would use nodemon
in the traditional method. In your backend terminal, you would use the command npx nodemon server.js
like this:
flip3 ~/react-starter-app/App/backend 1027$ npx nodemon server.js
[nodemon] 3.0.2
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node server.js`
Server running: http://flip3.engr.oregonstate.edu:65432...
# I made some changes and saved my code...
[nodemon] restarting due to changes...
[nodemon] starting `node server.js`
Server running: http://flip3.engr.oregonstate.edu:65432...
# I made some changes and saved my code...
[nodemon] restarting due to changes...
[nodemon] starting `node server.js`
Server running: http://flip3.engr.oregonstate.edu:65432...
# I made some changes and saved my code...
[nodemon] restarting due to changes...
[nodemon] starting `node server.js`
Server running: http://flip3.engr.oregonstate.edu:65432...
▌
This now allows us to utilize nodemon's
hot module reloding (HMR) which automatically restarts the server when code changes are saved. npx
is a command-line utility bundled with npm
that executes local node packages. It allows you to run any command available in a local node_modules
without globally installing the packages or adding them to your bash_profile
or bashrc
files. nodemon
is great, but we still have a problem in that our terminal has been taken over until control + C
has stopped the process. Another problem is that once we exit out of the terminal or the ssh session on flip, the server will stop running and we will no longer be able to view our active URL. This is where the package forever
will come in handy.
The package forever
allows us to run multiple processes forever which will run outside of a terminal or ssh instance. This package is currently already listed in both frontend/package.json
and backend/package.json
dependencies, so when you ran npm install
earlier, it should have installed that in both /frontend
and /backend
for you to be able to access. If for some reason this didn't happen, you can install it directly with npm install forever --save
.
You must run any forever commands from the root of your project (where server.js is located). If you don't it will fail. For this project, that is
/backend
or/frontend
For reasons beyond your control, running forever
is a bit more complex on the school's FLIP server. Here is how to make it easy, run the following command from the root of your project (/backend
or /frontend
):
alias forever='./node_modules/forever/bin/forever'
Now, whenever you run forever
from the root of any project that has the forever dependency installed, it will work, without fail. If you want to make this more permanent (not absolutely permanent), you can add this as a line towards the end of the ~/.bashrc
file in your home directory (notice the ~ squiggly) on the OSU FLIP server.
Now that we have forever
installed, let's look at the commands that you might use.
Running the command forever start server.js
, is similar to running the command node server.js
that we learned about prior to this. A difference is that it does not run in the foreground of our terminal, but instead runs in the background. Another difference is that forever
will automatically restart your application if there are any issues. With this command you can safely exit out of your flip ssh session and submit urls to canvas for other to view them. You can see below how our terminal prompt returns to the screen for us to be able to type further commands of our choice. Runing forever start server.js
can be done for any nodejs application that you want to start up.
flip3 ~/react-starter-app/App/backend 1032$ forever start server.js
# I have found that these warnings can be safely ignored...so far...
# The warnings will show up every time you run a forever command, they have not caused breaking issues for me yet...
warn: --minUptime not set. Defaulting to: 1000ms
warn: --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info: Forever processing file: server.js
(node:4115889) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:4115889) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
flip3 ~/react-starter-app/App/backend 1033$ ▌ # we can continue to do things in the terminal prompt!
To see a list of your current processes in forever
, you can use the command forever list
like this:
flip3 ~/react-starter-app/App/backend 1002$ forever list
(node:4144017) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:4144017) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
info: Forever processes running
data: uid command script forever pid id logfile uptime
data: [0] z8Fa /nfs/stak/users/maesz/.nvm/versions/node/v16.13.0/bin/node server.js 4143975 4144005 /nfs/stak/users/maesz/.forever/z8Fa.log 0:0:0:2.781
flip3 ~/react-starter-app/App/backend 1003$ ▌
Depending on what you have running, this might show multiple processes. There are many values that you can look at here including the location of your logs at logfile
, but also take note of the index value for this process which is [0]
, you will need this in the next section...
If you need to stop a process, like the one running above at index [0]
, you can use the command forever stop <index>
(ie - forever stop 0
) to accomplish this:
flip3 ~/react-starter-app/App/backend 1012$ forever stop 0
(node:4147301) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:4147301) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
info: Forever stopped process:
uid command script forever pid id logfile uptime
[0] 30Nc /nfs/stak/users/maesz/.nvm/versions/node/v16.13.0/bin/node server.js 4147194 4147226 /nfs/stak/users/maesz/.forever/30Nc.log 0:0:0:5.311
flip3 ~/react-starter-app/App/backend 1013$ ▌
This command can be used to stop any of the indexes (0, 1, 2, 3 , etc.)
In the same way that you can stop a process by its index, you can also restart a process by its index. This is useful if you made changes or something broke and you needed to restart.
flip3 ~/react-starter-app/App/backend 1027$ forever restart 0
(node:4158785) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:4158785) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
info: Forever restarted process(es):
data: uid command script forever pid id logfile uptime
data: [0] QH6j /nfs/stak/users/maesz/.nvm/versions/node/v16.13.0/bin/node server.js 4158690 4158716 /nfs/stak/users/maesz/.forever/QH6j.log 0:0:0:5.315
flip3 ~/react-starter-app/App/backend 1028$ ▌
This command can be used to restart any of the indexes (0, 1, 2, 3 , etc.)
This command will stop every single forever process that is running, use with caution! You will notice below that I happened to have four processes running and it stopped all of them. Again, use with caution!!
flip3 ~/react-starter-app/App/backend 1024$ forever stopall
(node:4155732) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:4155732) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
info: No forever processes running
info: Forever stopped processes:
data: uid command script forever pid id logfile uptime
data: [0] n34E /nfs/stak/users/maesz/.nvm/versions/node/v16.13.0/bin/node reactServer.cjs 4154645 4154671 /nfs/stak/users/maesz/.forever/n34E.log 0:0:1:12.748000000000005
data: [1] KgCi /nfs/stak/users/maesz/.nvm/versions/node/v16.13.0/bin/node server.js 4154727 4154768 /nfs/stak/users/maesz/.forever/KgCi.log 0:0:1:6.221000000000004
data: [2] 3z2l /nfs/stak/users/maesz/.nvm/versions/node/v16.13.0/bin/node controllers/peopleController.js 4155077 4155102 /nfs/stak/users/maesz/.forever/3z2l.log STOPPED
data: [3] eoEG /nfs/stak/users/maesz/.nvm/versions/node/v16.13.0/bin/node database/config.js 4155426 4155517 /nfs/stak/users/maesz/.forever/eoEG.log STOPPED
flip3 ~/react-starter-app/App/backend 1025$ ▌
As you can see, forever
is a very powerful tool that can help us while we develop the react application. Now let's take a look at how npm scripts will help us to simplify all these commands.
Inside our various package.json
files we can name and define any script that we want the npm
command to execute. Here is part of the backend/package.json
file where I have created two custom scripts called "start"
and "serve"
:
{
"name": "cs340-react-starter-app-backend",
"version": "1.0.0",
"description": "This is the backend express API that connects the frontend to the mariadb database",
"main": "server.js",
"scripts": {
"start": "nodemon server.js",
"serve": "npx forever start server.js"
},
...
If we run the command npm run start
, node package manager will look inside the "scripts"
section of the package.json
and find the "start"
command nodemon server.js
. Notice how we are utilizing nodemon
to take advantage of its development features like HMR. npm run start
will be the command that you run to start the backend server while you are actively working on code. This command will NOT permanently serve the api. So if you ran npm run start
(ie nodemon
) and exited out of the flip ssh session, your server would no longer be running.
flip3 ~/react-starter-app/App/backend 1004$ npm run start
> [email protected] start
> nodemon server.js
[nodemon] 3.0.2
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node server.js`
Server running: http://flip3.engr.oregonstate.edu:65432...
▌
When you need a url to stay active for days (in the case of submitting a project step), you will need to utilize the "serve"
script which is basically just the command npx forever start server.js
. As we learned in the prior section, forever will allow our program to run continuously in the background and restart it if necessary. To utilize this npm script, you will run the command npm run serve
like this:
flip3 ~/react-starter-app/App/backend 1011$ npm run serve
> [email protected] serve
> npx forever start server.js
warn: --minUptime not set. Defaulting to: 1000ms
warn: --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info: Forever processing file: server.js
(node:764027) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:764027) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
flip3 ~/ula_cs340/winter24/react-starter-app/App/backend 1012$
▌
The
npx
could potentially cause issues for some of you. If it does not work you could try editing the"serve"
script in thebackend/package.json
to look like this:"serve": "forever start server.js"
which just removes thenpx
command.
Now you should have a solid understanding of all the commands and scripts that can be used to run the /backend
server. We will now take a closer look at the scripts specific to the /frontend
vite react project.
We already covered the "start"
script in a the prior section Command - npm run start. In our /frontend/package.json
you will notice a lot of other scripts that will be very useful for development. Again, you are allowed to modify these scripts to your needs, but I would recommend leaving the "start"
and "build"
commands as is because they are utilizing built in vite tooling.
{
"name": "cs340-react-starter-app-frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"start": "vite",
"build": "vite build",
"serve": "npx forever start reactServer.cjs",
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
- What It Does: This script starts a local development server for your Vite React application.
- How It Works: By running
npm run start
, Vite serves your application with hot module replacement (HMR), which means your changes will be updated live without needing to refresh your browser. This essentially runs the terminal commandvite
. - Use Case: Use this command while actively developing your application to see your changes in real-time.
- This was covered in the prior Option 1 - Local Only section
- What It Does: Builds your application for production.
- How It Works: Running
npm run build
compiles your React application into static files optimized for production, typically into a/dist
directory. - Use Case: Run this script when you are ready to deploy your application, creating a version that's optimized for speed and efficiency.
- This is explained more specifically in the next Build and Deploy Section
- What It Does: Uses
forever
to serve your production build continuously via a custom express application calledreactServer.cjs
.- Note: commonJS is required here because of our package.json definition of
"type": "module"
. It is also possible to use ES6, but that is not covered in this guide.
- Note: commonJS is required here because of our package.json definition of
- How It Works: After building your application with
npm run build
, run this scriptnpm run serve
to start the apireactServer.cjs
that serves your built application located in the/dist
folder. The forever tool ensures that this server runs continuously, even if the script crashes or the server is restarted. - Use Case: Ideal for when you need your built application to be accessible over the internet for extended periods (For example, when submitting a project step).
- This is explained more specifically in the next Build and Deploy Section
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0"
(NOT REQUIRED TO USE)
- What It Does: Lints your JavaScript and JSX files to ensure code quality and consistency.
- How It Works: This script runs ESLint on all .js and .jsx files in your project, enforcing your coding standards. Run it with
npm run lint
. The --report-unused-disable-directives flag reports ESLint disable comments that don't actually suppress any errors, and --max-warnings 0 treats warnings as errors, failing the script if any are found. - Use Case: Run this command to check for and fix linting errors in your codebase, ideally before committing your code.
- This is automatically provided by vite and is not required, but you might find useful. This guide will not discuss this script further, use at your own risk.
- What It Does: Serves your production build locally for testing.
- How It Works: After building your project with
npm run build
, this script serves the production version of your application from a local server, allowing you to test the built site before deploying it. Run this withnpm run preview
. - Use Case: Use this to preview the final version of your site in a production-like environment, verifying that everything works as expected before an actual deployment.
- This is automatically provided by vite and is not required, but you might find useful. This guide will not discuss this script further, use at your own risk.
Remember, you can always edit these scripts to fit your project needs. These are not set in stone, and they are more like preferences that you get to decide on. For example, you might find that you are not using forever
but instead using pm2
(not covered in this guide). This would require you to change the "serve"
script to reflect your use of the pm2
package. In theory you can create and name any script you want. For example, if you wanted to use nodemon
to test if reactServer.cjs
is working prior to serving it with forever
, you would create a new script inside the package.json file like this: "nodemon-server": "nodemon reactServer.cjs"
and then run it in the terminal with the command npm run nodemon-server
.
Implementing the nodemon-server script will be left as an exercise for the student. It may require additional configuration.
Now that we have a high level overview of the /frontend
npm scripts, let's take a closer look at how the "build"
and "serve"
script will work for this react project.
There are hundreds of ways to serve your applications which include but are not limited to the flip servers, netlify, vercel, heroku, google cloud, or even hardware solutions like raspberry pis. This guide will show you how to serve specifically with vite, express, forever, and the flip server. You essentially build your react application with vite build
then statically serve the build folder (/dist
) using the express server reactServer.cjs
. This express server will run in parallel to your /backend
express mariadb server that we configured in the prior section. At the end of the day, you are running two servers at two different ports.
This video goes through a very simple react/express application that covers the philosophy of this build process for the frontend server. Serve a React app from an Express server | React frontend and Express API setup in 1 project!
If you have not done so already, open up your /frontend/.env
file and set a port number for the REACT_SERVER_PORT
variable and save the file. If you recall from the Frontend Setup (Vite) section, our .env file sould look like this (change the ports to ones you are going to use)
VITE_API_URL='http://flip3.engr.oregonstate.edu:8500/api/' # Change this url to match your backend express api url and port.
VITE_PORT=8501 # Set a port number between:[1024 < PORT < 65535], this should NOT be the same as the API port.
REACT_SERVER_PORT=6061 # This is the port used in the /frontend/reactServer.js to host your '/dist' build folder. [1024 < PORT < 65535]
Looking inside of the file reactServer.cjs
, we will find a very small express server that has one function. This will server our static build files located inside the /dist
folder.
// reactServer.cjs
// Uses common javascript to serve the react build folder (/dist)
const express = require('express');
const path = require('path');
const app = express();
require("dotenv").config();
// Use the custom 'REACT_SERVER_PORT' port from .env, with a fallback to 3001
const PORT = process.env.REACT_SERVER_PORT || 3001;
// Serve the static files from the React app located in the build folder '/dist'
// React router will take over frontend routing
app.use(express.static(path.join(__dirname, 'dist')));
// Handles any requests that don't match the ones above to return the React app
// A request to '/nonExist' will redirect to the index.html where react router takes over at '/'
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'dist', 'index.html'));
});
app.listen(PORT, () => {
// Change this text to whatever FLIP server you're on
console.log(`Server running: http://flip3.engr.oregonstate.edu:${PORT}...`);
});
Please take notice of two things here in the reactServer.cjs
:
- We create a
PORT
variable for the server to run on that peers into our .env file and pulls out the dotenv variableREACT_SERVER_PORT
that we just defined. - We use
app.use(...)
andapp.get('*', ...)
to point every url endpoint to our static files located at/dist
and that folder'sindex.html
.
We will see how this works soon, but first we must build our /dist
folder. Use the command npm run build
to do this.
# Build the '/dist' folder...
flip3 ~/react-starter-app/App/frontend 1022$ npm run build
> [email protected] build
> vite build
vite v5.1.4 building for production...
✓ 93 modules transformed.
dist/index.html 0.45 kB │ gzip: 0.30 kB
dist/assets/index-vBDqSkg8.css 0.11 kB │ gzip: 0.11 kB
dist/assets/index-bVtY9NLx.js 201.19 kB │ gzip: 67.30 kB
✓ built in 6.16s
flip3 ~/react-starter-app/App/frontend 1023$ ▌
You should now see a
/dist
folder was created in the/frontend
directory!
Every time you run the command npm run build
it will replace your old /dist
with a new version containing all of your newly saved react code and assets. Be careful with this command becasue you will lose the old version of /dist
that you may have running on your older server.
Now that you have built your /dist
folder, this static build can be served hundreds of different ways. Since we are already running the express backend on a different flip port, we will use our frontend express server reactServer.cjs
to serve the /dist
build folder at a port of our choosing. There is a serve command built into the frontend package.json that will automatically use forever to start up reactServer.cjs
.
# Serve the frontend dist build
flip1 ~/react-starter-app/App/frontend 583$ npm run serve
> [email protected] serve
> npx forever start reactServer.cjs
warn: --minUptime not set. Defaulting to: 1000ms
warn: --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info: Forever processing file: reactServer.cjs
(node:1702055) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:1702055) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
flip1 ~/react-starter-app/App/frontend 584$ ▌
Assuming that you have the backend server running, if you run the command
forever list
you should see both processes for frontend and backend.
Your Website is now visible at the flip server and port number that you set up in
.env
.
flip1 ~/react-starter-app/App/frontend 584$ forever list
(node:1702689) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
(Use `node --trace-warnings ...` to show where the warning was created)
(node:1702689) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
info: Forever processes running
data: uid command script forever pid id logfile uptime
data: [0] xdT7 /nfs/stak/users/maesz/.nvm/versions/node/v16.13.0/bin/node server.js 1701949 1701965 /nfs/stak/users/maesz/.forever/xdT7.log 0:0:10:59.63900000000001
data: [1] 9f1d /nfs/stak/users/maesz/.nvm/versions/node/v16.13.0/bin/node reactServer.cjs 1702085 1702100 /nfs/stak/users/maesz/.forever/9f1d.log 0:0:10:41.789999999999964
flip1 ~/react-starter-app/App/frontend 585$ ▌
Here are some images of what the stock build of this website will look like.