React with Vite and Express API, using Node.js and TypeScript

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 tlom-viteserve --cwd /home/nate/code/tlom2/client -- -c "serve -s dist --listen 4173"

From the api/ directory:

pm2 start index.js

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

Then modify

sudo nano /etc/nginx/sites-available/default

with:

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /home/youruser/your-project-folder/frontend/dist;
index index.html;
location / {
try_files $uri /index.html;
}
location /api/ {
proxy_pass http://localhost:5000/;
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;
}
}

Then restart nginx:

sudo nginx -t && sudo systemctl restart nginx