Ravindra BagaleCourses & study guides मराठी Track your progress

Chapter 12: Technology Stacks — LAMP, LEMP, MEAN, MERN

12.7 MERN: React frontend served by Nginx

Come on, now let's build the frontend. See how React code turns into simple static files that Nginx serves easily.

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 the React/Angular app on your laptop (npm run build) and upload only the dist/ folder with scp. A build needs a lot of RAM and a 1 GiB instance can hang. The server should only serve files, not build them – CI/CD pipelines in companies work the same way.

Lab

Chala, React notes app build kara, dist/ /var/www/mern madhe copy kara aani Nginx config lava. http://<PUBLIC_IP>/ var don notes add kara, page refresh kara aani notes MongoDB madhun parat distat ka te bagha.

Got it? A React build is just static files – Nginx serves them and sends /api to Node. Now let's move on to Angular.