Ravindra BagaleCourses & study guides

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

12.8 MEAN: Angular frontend served by Nginx

The backend (Express + MongoDB on port 5000) is exactly the same as in 12.6.

Build the Angular app

export NG_CLI_ANALYTICS=false
sudo mkdir -p /opt/mean-frontend && sudo chown $USER:$USER /opt/mean-frontend
cd /opt/mean-frontend
npx -y @angular/cli@latest new frontend --defaults --ssr=false --skip-git --interactive=false
cd frontend
ls src/app/

Replace the root component. In Angular 20 and newer the file is src/app/app.ts (class App); in Angular 17–19 it is src/app/app.component.ts (class AppComponent — change the class name in the code below to match). This version uses signals, so it works whether or not the project uses zone.js:

cat > src/app/app.ts <<'EOF'
import { Component, OnInit, signal } from '@angular/core';

interface Note { _id: string; text: string; }

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <div style="font-family:Arial;max-width:600px;margin:40px auto">
      <h1>MEAN Notes on AWS EC2</h1>
      <input #box placeholder="Write a note">
      <button (click)="add(box.value); box.value = ''">Add</button>
      <ul>
        @for (n of notes(); track n._id) { <li>{{ n.text }}</li> }
      </ul>
    </div>
  `,
})
export class App implements OnInit {
  notes = signal<Note[]>([]);

  async ngOnInit() { await this.load(); }

  async load() {
    const res = await fetch('/api/notes');
    this.notes.set(await res.json());
  }

  async add(text: string) {
    if (!text.trim()) return;
    await fetch('/api/notes', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ text }),
    });
    await this.load();
  }
}
EOF

npx ng build                                  # output: dist/frontend/browser/
sudo mkdir -p /var/www/mean
sudo cp -r dist/frontend/browser/* /var/www/mean/
sudo restorecon -Rv /var/www/mean 2>/dev/null   # CentOS only

Angular output folder

Angular 17+ puts the production build in dist/<project-name>/browser/. Older versions used dist/<project-name>/. Check with ls dist/*.

Nginx configuration for MEAN

Identical to MERN — only the root folder changes:

sudo sed 's#/var/www/mern#/var/www/mean#' /etc/nginx/conf.d/mern.conf | sudo tee /etc/nginx/conf.d/mean.conf > /dev/null
sudo rm /etc/nginx/conf.d/mern.conf           # keep only one of them for the same server_name
sudo nginx -t && sudo service nginx reload

(Angular's built assets are not in /assets/, so that caching block simply does nothing — harmless.)