Install the Latest Node.js
sudo apt install nodejs npm
sudo npm install n -g
For the latest stable version of Node JS:
sudo n stable
node --version
Create a New React App with Vite
Install Node.js, then in the directory where you want to install (often within project-name/client):
npm create vite@latest
npm run dev
Clone Existing React App
To clone a React project from Github, you clone it, then use the following command from the project subdirectory (to install it, which I don’t totally understand, but it works):
npm install --f
Move Entire App to a Subdirectory
You’ll want the React app to be contained in a subdirectory /client and the Express API contained in a subdirectory /api or /server. You can move the entire React app by moving all files into a subdirectory with the following commands:
mkdir client
git mv -k * client/
Create the Express API (mostly from ChatGPT)
mkdir api
cd api
npm init -y
Install express. Install mysql2 only if you are going to connect to a database:
npm install express cors dotenv mysql2
npm install --save-dev typescript ts-node nodemon @types/node @types/express @types/cors
UPDATE: ts-node is apparently a higher-performance version of tsx. I found ts-node to be a pain, so next time, use tsx instead.
npx tsc --init
Modify tsconfig.json for better compatibility:
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"moduleResolution": "node"
}
Add the following lines to package.json, scripts section to run typescript files directly with ts-node by compiling with tsc then running the dist/index.js file:
… ,
"type": "module",
"scripts": {
"dev": "ts-node src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
Make src/ directory:
mkdir src
cd src
Within src/, make index.ts and write the express API TS code, then run it with:
npm run dev
Running the Two Servers, API + React App
When they are separate, run the API from the api/ directory with:
npm run dev
and run the React app made with Vite from the client/ directory with:
npx vite
Deploy to a VPS
Fresh install an Ubuntu server and create non-root superuser per these instructions. Then:
sudo apt install -y nodejs npm nginx
sudo npm install -g serve pm2
Build and Run Frontend Manually
Going to frontend directory, then:
npm install
rm -rf dist (if necessary to clear previous build)
npm run build
package.json should be: "scripts": { "dev": "vite", "build": "tsc -b && vite build", ... }
serve -s dist --listen 4173
Run the Backend Manually
node index.js
CORS and .env Note
It is worth setting up – from the beginning – permissive CORS that utilizes .env. You can get pretty far ignoring CORS and .env in your initial local dev environment, but you do encounter CORS even in the local environment from the start. Facing and understanding the concepts of .env and CORS up front saves time overall.
Also using .env up front helps separate the environmental factors so you understand how things are working better.
Set up pm2
From the client/ directory:
pm2 start bash --name tv3clock-viteserve --cwd /home/nate/code/time-v3-astroclock4/client -- -c "npx serve -s dist --listen 6171"
From the api/ directory:
pm2 start index.js --name tv3clock-api
Set up pm2 for reboot:
pm2 save
pm2 startup
Then copy-paste the long command it gives you, per instructions.
Nginx as a Reverse Proxy
It is normal to run the sites with pm2 and use nginx to direct traffic to them.
Then add file
sudo nano /etc/nginx/sites-available/my-sites.conf
and edit with:
# -------------------------------
# Upstream definitions for PM2 apps
# -------------------------------
upstream tv3_frontend {
server 127.0.0.1:6171;
}
upstream tv3_api {
server 127.0.0.1:5001;
}
upstream tlom_frontend {
server 127.0.0.1:6172;
}
upstream tlom_api {
server 127.0.0.1:5002;
}
# -------------------------------
# timev3.com server block
# -------------------------------
server {
server_name timev3.com www.timev3.com;
# API proxy
location /api/ {
proxy_pass http://tv3_api/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# Frontend proxy
location / {
proxy_pass http://tv3_frontend/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
listen 80;
}
# -------------------------------
# mytlom.com server block
# -------------------------------
server {
server_name mytlom.com www.mytlom.com;
# API proxy
location /api/ {
proxy_pass http://tlom_api/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# Frontend proxy
location / {
proxy_pass http://tlom_frontend/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
listen 80;
}
Run a test on the new nginx configuration:
sudo nginx -t
Restart nginx:
sudo systemctl restart nginx