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.

sudo apt install -y nodejs npm nginx

sudo npm install -g pm2

Build the front end by going to front end directory, then:

npm install

npx vite build

not:

npm run build (no, not this!)

(I think I may have to do this every time I edit the code)

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

At this point the front-end should be working, but not the back-end yet. From the backend directory:

npx tsc (because I use typescript)

ls dist (to confirm the build output)

pm2 start dist/index.js --name tlom-api

pm2 save
pm2 startup

pm2 says to copy-paste this and run it:

sudo env PATH=$PATH:/usr/bin /usr/local/lib/node_modules/pm2/bin/pm2 startup systemd -u nate --hp /home/nate