Chapter 12: Technology Stacks — LAMP, LEMP, MEAN, MERN
12.7 MERN: React frontend served by Nginx
Chala, aata frontend build karuya. Bagha how the React code becomes simple static files that Nginx can serve.
Build the React app (Vite)
cd /opt
sudo mkdir -p /opt/mern-frontend && sudo chown $USER:$USER /opt/mern-frontend
cd /opt/mern-frontend
npm create vite@latest . -- --template react
npm install
(If create vite asks any questions, accept the defaults and do not start the dev server.)
Replace src/App.jsx with a small notes UI that talks to /api:
cat > src/App.jsx <<'EOF'
import { useEffect, useState } from "react";
export default function App() {
const [notes, setNotes] = useState([]);
const [text, setText] = useState("");
const load = () => fetch("/api/notes").then((r) => r.json()).then(setNotes);
useEffect(() => { load(); }, []);
const add = async (e) => {
e.preventDefault();
if (!text.trim()) return;
await fetch("/api/notes", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text }),
});
setText("");
load();
};
return (
<div style={{ fontFamily: "Arial", maxWidth: 600, margin: "40px auto" }}>
<h1>MERN Notes on AWS EC2</h1>
<form onSubmit={add}>
<input value={text} onChange={(e) => setText(e.target.value)} placeholder="Write a note" />
<button type="submit">Add</button>
</form>
<ul>{notes.map((n) => <li key={n._id}>{n.text}</li>)}</ul>
</div>
);
}
EOF
npm run build # creates the dist/ folder
sudo mkdir -p /var/www/mern
sudo cp -r dist/* /var/www/mern/
sudo restorecon -Rv /var/www/mern 2>/dev/null # CentOS only
(With Create React App the output folder is build/ instead of dist/.)
Nginx configuration for MERN
# AL2023 / CentOS: /etc/nginx/conf.d/mern.conf
# Ubuntu: save as /etc/nginx/sites-available/mern, symlink into sites-enabled, remove 'default',
# and use "listen 80 default_server;" + "server_name _;"
sudo tee /etc/nginx/conf.d/mern.conf > /dev/null <<'EOF'
server {
listen 80;
server_name YOUR_SERVER_NAME;
root /var/www/mern;
index index.html;
# Single Page App: unknown paths go to index.html (client-side routing)
location / {
try_files $uri $uri/ /index.html;
}
# API requests go to the Node/Express backend
location /api/ {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Cache hashed static assets
location /assets/ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
EOF
sudo sed -i "s/YOUR_SERVER_NAME/$(curl -s https://checkip.amazonaws.com)/" /etc/nginx/conf.d/mern.conf
sudo setsebool -P httpd_can_network_connect 1 2>/dev/null # CentOS only
sudo nginx -t && sudo service nginx reload
Open http://<PUBLIC_IP>/, add notes, refresh — they persist in MongoDB.
Ravindra Bagale's Tip
On a small t3.micro, build your React/Angular app on your laptop (npm run build) and upload only the dist/ folder with scp. Builds need a lot of RAM and can freeze a 1 GiB instance. The server should only serve files, not build them — this is also how CI/CD pipelines work in companies.
Ravindra Bagale's Tip – मराठी
छोट्या t3.micro वर React/Angular app तुमच्या laptop वरच build करा (npm run build) आणि फक्त dist/ folder scp ने upload करा. Build ला खूप RAM लागते आणि 1 GiB चा instance अडकू शकतो. Server ने फक्त files serve कराव्यात, build नाही — companies मधल्या CI/CD pipelines पण असंच काम करतात.
Ravindra Bagale's Tip – हिंदी
छोटे t3.micro पर React/Angular app अपने laptop पर ही build करो (npm run build) और सिर्फ़ dist/ folder scp से upload करो. Build में बहुत RAM लगती है और 1 GiB का instance hang हो सकता है. Server को सिर्फ़ files serve करनी चाहिए, build नहीं — companies में CI/CD pipelines भी ऐसे ही काम करती हैं.