KEMBAR78
Full Stack App Using MEAN Stack | PDF | Information Technology | Systems Architecture
0% found this document useful (0 votes)
35 views16 pages

Full Stack App Using MEAN Stack

This document provides a detailed guide on creating a full-stack application using Angular 19, Express, Node.js, and MongoDB with pnpm as the package manager. It covers setting up a monorepo structure, configuring the backend with Express and MongoDB, and developing the Angular frontend, including creating components and services for data handling. The guide also includes steps for adding a form to create new items and updating routing for navigation.

Uploaded by

Dinesh Anbarasan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views16 pages

Full Stack App Using MEAN Stack

This document provides a detailed guide on creating a full-stack application using Angular 19, Express, Node.js, and MongoDB with pnpm as the package manager. It covers setting up a monorepo structure, configuring the backend with Express and MongoDB, and developing the Angular frontend, including creating components and services for data handling. The guide also includes steps for adding a form to create new items and updating routing for navigation.

Uploaded by

Dinesh Anbarasan
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 16

Creating a Full-Stack Application with Angular 19,

Express, Node.js, and MongoDB using pnpm


This comprehensive guide walks you through creating a basic full-stack application that
retrieves data from MongoDB and displays it in an Angular 19 web application. We'll be using
pnpm as our package manager and setting up a monorepo structure to organize our codebase
effectively.

Setting Up the Monorepo Structure


Let's start by creating a monorepo structure using pnpm to manage both our frontend and
backend applications.

Prerequisites
First, ensure you have Node.js installed:

node -v

If Node.js isn't installed, download it from the official website. Next, install pnpm globally:

# Using npm
npm install -g pnpm

# Or using Brew if available


brew install pnpm

Verify the installation:

pnpm -v

Creating the Monorepo


1. Create a root directory for your project:

mkdir mean-fullstack-app
cd mean-fullstack-app

2. Initialize pnpm in the root directory:

pnpm init
3. Create a pnpm workspace configuration file:

# Create pnpm-workspace.yaml file


echo "packages:
- 'apps/*'
- 'packages/*'" > pnpm-workspace.yaml

4. Set up the directory structure:

mkdir -p apps/server apps/client packages/tsconfig

This will create a structure like this:

mean-fullstack-app/
├── apps/
│ ├── client/
│ └── server/
├── packages/
│ └── tsconfig/
├── package.json
└── pnpm-workspace.yaml

This monorepo approach allows us to manage both the frontend and backend code within a
single repository while sharing common configurations and packages [1] .

Setting Up the Backend (Express.js and MongoDB)


Let's start with setting up the backend using Express.js and MongoDB.

Initialize the Server Project


1. Navigate to the server directory:

cd apps/server

2. Initialize a new package.json file:

pnpm init

3. Open the generated package.json and modify the main field to "src/index.js" [1] .

Install Backend Dependencies

pnpm add express mongoose cors dotenv


pnpm add -D typescript @types/express @types/node @types/cors ts-node nodemon
Configure TypeScript
1. Create a tsconfig.json file:

npx tsc --init

2. Update the tsconfig.json with appropriate settings:

{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"outDir": "./dist",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}

3. Create a src directory for your server code:

mkdir src

Create the Basic Express Server


Create a file src/index.ts:

import express from 'express';


import mongoose from 'mongoose';
import cors from 'cors';
import dotenv from 'dotenv';

// Load environment variables


dotenv.config();

// Initialize Express app


const app = express();
const PORT = process.env.PORT || 3000;

// Middleware
app.use(cors());
app.use(express.json());

// MongoDB Connection
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/meanapp';

mongoose.connect(MONGODB_URI)
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.error('MongoDB connection error:', err));

// Basic route
app.get('/api', (req, res) => {
res.json({ message: 'Welcome to the MEAN Stack API' });
});

// Start server
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

Create MongoDB Model and Routes


1. Create a models directory:

mkdir src/models

2. Create a model for your data (e.g., src/models/item.ts):

import mongoose, { Schema, Document } from 'mongoose';

export interface IItem extends Document {


name: string;
description: string;
createdAt: Date;
}

const ItemSchema: Schema = new Schema({


name: { type: String, required: true },
description: { type: String, required: true },
createdAt: { type: Date, default: Date.now }
});

export default mongoose.model<IItem>('Item', ItemSchema);

3. Create a routes directory:

mkdir src/routes

4. Create routes for CRUD operations (e.g., src/routes/items.ts):

import express, { Router } from 'express';


import Item from '../models/item';

const router: Router = express.Router();

// Get all items


router.get('/', async (req, res) => {
try {
const items = await Item.find();
res.json(items);
} catch (err) {
res.status(500).json({ message: 'Server error' });
}
});

// Create a new item


router.post('/', async (req, res) => {
try {
const newItem = new Item({
name: req.body.name,
description: req.body.description
});
const savedItem = await newItem.save();
res.status(201).json(savedItem);
} catch (err) {
res.status(400).json({ message: 'Invalid data' });
}
});

export default router;

5. Update your src/index.ts to include the routes:

// ... other imports


import itemRoutes from './routes/items';

// ... middleware setup

// Routes
app.use('/api/items', itemRoutes);

// ... server startup

6. Add npm scripts to package.json:

"scripts": {
"dev": "nodemon src/index.ts",
"build": "tsc",
"start": "node dist/index.js"
}

Create Environment Variables


Create a .env file in the server directory:

PORT=3000
MONGODB_URI=mongodb://localhost:27017/meanapp

This completes the backend setup. The server is now configured to connect to MongoDB and
provide API endpoints for items [2] [3] .
Setting Up the Angular 19 Frontend
Now, let's set up the Angular 19 frontend application.

Create a New Angular Project


1. Navigate to the client directory:

cd ../../apps/client

2. Install Angular CLI globally:

pnpm add -g @angular/cli

3. Create a new Angular application:

ng new client --standalone --routing --style=scss


cd client

Configure Proxy for API Communication


Create a proxy.conf.json file in the client directory:

{
"/api": {
"target": "http://localhost:3000",
"secure": false,
"logLevel": "debug"
}
}

Update the angular.json file to use the proxy configuration:

"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"proxyConfig": "proxy.conf.json"
},
// ... other config
}

Create Service to Fetch Data


1. Generate a service to communicate with the API:

ng generate service services/item

2. Update the service (src/app/services/item.service.ts):


import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface Item {


_id: string;
name: string;
description: string;
createdAt: Date;
}

@Injectable({
providedIn: 'root'
})
export class ItemService {
private apiUrl = '/api/items';

constructor(private http: HttpClient) { }

getItems(): Observable<Item[]> {
return this.http.get<Item[]>(this.apiUrl);
}

addItem(item: { name: string; description: string }): Observable<Item> {


return this.http.post<Item>(this.apiUrl, item);
}
}

Create Components to Display Data


1. Generate a component to display the items:

ng generate component components/item-list

2. Update the component (src/app/components/item-list/item-list.component.ts):

import { Component, OnInit } from '@angular/core';


import { ItemService, Item } from '../../services/item.service';
import { CommonModule } from '@angular/common';

@Component({
selector: 'app-item-list',
standalone: true,
imports: [CommonModule],
templateUrl: './item-list.component.html',
styleUrls: ['./item-list.component.scss']
})
export class ItemListComponent implements OnInit {
items: Item[] = [];
loading = true;
error: string | null = null;

constructor(private itemService: ItemService) { }


ngOnInit(): void {
this.fetchItems();
}

fetchItems(): void {
this.itemService.getItems().subscribe({
next: (data) => {
this.items = data;
this.loading = false;
},
error: (err) => {
this.error = 'Failed to load items';
this.loading = false;
console.error(err);
}
});
}
}

3. Update the template file (src/app/components/item-list/item-list.component.html):

<div class="container">
<h2>Items List</h2>

<div *ngIf="loading">Loading...</div>

<div *ngIf="error" class="error">{{ error }}</div>

<div *ngIf="!loading && !error">


<div *ngIf="items.length === 0">No items found.</div>

<div *ngIf="items.length > 0" class="items-grid">


<div *ngFor="let item of items" class="item-card">
<h3>{{ item.name }}</h3>
<p>{{ item.description }}</p>
<small>Created: {{ item.createdAt | date }}</small>
</div>
</div>
</div>
</div>

4. Add some basic styling (src/app/components/item-list/item-list.component.scss):

.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}

.items-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-top: 20px;
}

.item-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.error {
color: red;
font-weight: bold;
}

Update the App Module to Include HTTP Client


Update src/app/app.config.ts to include HttpClientModule:

import { ApplicationConfig } from '@angular/core';


import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';

import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {


providers: [
provideRouter(routes),
provideHttpClient()
]
};

Update the App Routes


Update src/app/app.routes.ts to include the ItemListComponent:

import { Routes } from '@angular/router';


import { ItemListComponent } from './components/item-list/item-list.component';

export const routes: Routes = [


{ path: '', redirectTo: 'items', pathMatch: 'full' },
{ path: 'items', component: ItemListComponent }
];

Update App Component


Update src/app/app.component.html:

<header>
<h1>MEAN Stack Application</h1>
</header>
<main>
<router-outlet></router-outlet>
</main>

<footer>
<p>© 2025 MEAN Stack Demo</p>
</footer>

This completes the Angular frontend setup [4] [5] .

Creating a Form to Add New Items


Let's enhance our application by adding a form to create new items.

Create an Item Form Component


1. Generate a new component:

ng generate component components/item-form

2. Update the component (src/app/components/item-form/item-form.component.ts):

import { Component } from '@angular/core';


import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
import { ItemService } from '../../services/item.service';
import { CommonModule } from '@angular/common';

@Component({
selector: 'app-item-form',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
templateUrl: './item-form.component.html',
styleUrls: ['./item-form.component.scss']
})
export class ItemFormComponent {
itemForm: FormGroup;
submitting = false;
error: string | null = null;
success = false;

constructor(
private fb: FormBuilder,
private itemService: ItemService
) {
this.itemForm = this.fb.group({
name: ['', [Validators.required, Validators.minLength(3)]],
description: ['', [Validators.required, Validators.minLength(5)]]
});
}

onSubmit(): void {
if (this.itemForm.invalid) {
return;
}
this.submitting = true;
this.error = null;
this.success = false;

this.itemService.addItem(this.itemForm.value).subscribe({
next: () => {
this.submitting = false;
this.success = true;
this.itemForm.reset();
},
error: (err) => {
this.submitting = false;
this.error = 'Failed to add item';
console.error(err);
}
});
}
}

3. Create the template (src/app/components/item-form/item-form.component.html):

<div class="form-container">
<h2>Add New Item</h2>

<form [formGroup]="itemForm" (ngSubmit)="onSubmit()">


<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" formControlName="name">
<div *ngIf="itemForm.get('name')?.invalid && itemForm.get('name')?.touched" class="
Name is required and must be at least 3 characters long.
</div>
</div>

<div class="form-group">
<label for="description">Description</label>
<textarea id="description" formControlName="description" rows="4"></textarea>
<div *ngIf="itemForm.get('description')?.invalid && itemForm.get('description')?.to
Description is required and must be at least 5 characters long.
</div>
</div>

<button type="submit" [disabled]="itemForm.invalid || submitting">


{{ submitting ? 'Adding...' : 'Add Item' }}
</button>
</form>

<div *ngIf="error" class="error-message">{{ error }}</div>


<div *ngIf="success" class="success-message">Item added successfully!</div>
</div>

4. Add some styling (src/app/components/item-form/item-form.component.scss):

.form-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.form-group {
margin-bottom: 20px;
}

label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}

input, textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}

button {
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}

button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}

.error {
color: red;
font-size: 12px;
margin-top: 5px;
}

.error-message {
color: red;
margin-top: 20px;
}

.success-message {
color: green;
margin-top: 20px;
}

5. Update src/app/app.routes.ts to include the form component:


import { Routes } from '@angular/router';
import { ItemListComponent } from './components/item-list/item-list.component';
import { ItemFormComponent } from './components/item-form/item-form.component';

export const routes: Routes = [


{ path: '', redirectTo: 'items', pathMatch: 'full' },
{ path: 'items', component: ItemListComponent },
{ path: 'add-item', component: ItemFormComponent }
];

6. Update the app component (src/app/app.component.html) to include navigation:

<header>
<h1>MEAN Stack Application</h1>
<nav>
<a routerLink="/items">View Items</a>
<a routerLink="/add-item">Add Item</a>
</nav>
</header>

<main>
<router-outlet></router-outlet>
</main>

<footer>
<p>© 2025 MEAN Stack Demo</p>
</footer>

Setting Up Scripts for the Entire Project


Let's update the root package.json to include scripts that run both the frontend and backend:

{
"name": "mean-fullstack-app",
"version": "1.0.0",
"scripts": {
"start:server": "cd apps/server && pnpm run dev",
"start:client": "cd apps/client && ng serve",
"dev": "concurrently \"pnpm run start:server\" \"pnpm run start:client\"",
"build:server": "cd apps/server && pnpm run build",
"build:client": "cd apps/client && ng build",
"build": "pnpm run build:server && pnpm run build:client"
},
"devDependencies": {
"concurrently": "^8.2.0"
}
}

Install the concurrently package:

pnpm add -D concurrently


Running the Application
1. Start MongoDB:
If using MongoDB locally, make sure your MongoDB server is running.
2. From the root directory, start both the frontend and backend:

pnpm run dev

This will concurrently run the Express.js backend server and the Angular frontend development
server.
3. Open your browser and navigate to:
Frontend: http://localhost:4200
Backend API: http://localhost:3000/api

Conclusion
You've successfully created a full-stack MEAN application using pnpm as the package
manager. The application:
1. Uses a monorepo structure to organize frontend and backend code
2. Has a Node.js/Express.js backend connected to MongoDB
3. Features an Angular 19 frontend that fetches and displays data from the API
4. Includes a form to add new items to the database
This basic application provides a foundation that you can extend with more features, such as
authentication, additional CRUD operations, and more complex data models. The monorepo
approach makes it easier to share code between frontend and backend, improving
maintainability and development workflow.
By following these steps, you've created a modern full-stack JavaScript application that
demonstrates the power and flexibility of the MEAN stack with the efficiency of pnpm for
package management.

Understanding the Missing app.module.ts in


Modern Angular Projects
If you've noticed that the app.module.ts file is missing from your Angular project's src/app folder,
you're encountering one of the significant changes introduced in recent versions of Angular. This
isn't an error – it's by design in newer Angular applications.
Why app.module.ts is Missing
Starting from Angular v17 (released in November 2023), the Angular team made "Standalone
Components" the new default for applications created with the Angular CLI. This means:
New Angular projects no longer generate the traditional app.module.ts file automatically
The application structure now relies on standalone components that don't require NgModule
declarations
This change was implemented to simplify Angular development by reducing boilerplate
code [6] [7]

Your Options for Handling This Situation

Option 1: Embrace Standalone Components (Recommended)


The Angular team strongly recommends using standalone components as they are easier to
understand, require less boilerplate, and represent the future direction of Angular development.
With standalone components, you directly import what you need in each component rather than
declaring everything in modules [6] .

Option 2: Create a New Project with NgModules


If you prefer or require the traditional NgModule approach (perhaps for compatibility with
existing code or tutorials):

ng new your-project-name --no-standalone

This command will generate a new project with the traditional module-based structure, including
app.module.ts [7] [8] .

Option 3: Manually Add app.module.ts to Your Existing Project


If you need to add an app.module.ts file to your existing standalone project:
1. Create a new file named app.module.ts in your src/app directory
2. Add the following basic structure (customize as needed) [6] :

import { NgModule } from '@angular/core';


import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

@NgModule({
declarations: [
AppComponent
// Add other components here
],
imports: [
BrowserModule,
// Add other modules here
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

3. Update your main.ts file to use this module instead of the standalone bootstrap

Understanding Angular's Evolution


This change reflects Angular's evolution toward a simpler component model. The standalone
approach allows developers to:
Directly import dependencies where they're needed
Avoid the cognitive overhead of managing NgModules
Benefit from better tree-shaking for smaller bundle sizes
Create more portable, self-contained components [6] [9]

Conclusion
The missing app.module.ts file is not an issue but a reflection of Angular's move toward a more
streamlined development experience. You can either adapt to this new approach (recommended
for new projects) or use the --no-standalone flag when creating new projects if you prefer the
traditional module-based structure.
If you're following older tutorials or courses that reference app.module.ts, keep in mind they were
likely created before this change in Angular 17, and you'll need to make appropriate adjustments
or create a non-standalone project.

1. https://ayoubkhial.com/blog/mean-web-app-part-2-express-meets-typescript
2. https://github.com/DavideViolante/Angular-Full-Stack
3. https://www.mongodb.com/resources/languages/mongodb-and-npm-tutorial
4. https://www.youtube.com/watch?v=jJAkjtXLyLw
5. https://www.youtube.com/watch?v=gYkxU1eBEYk
6. https://stackoverflow.com/questions/77454741/why-doesnt-app-module-exist-in-angular-17
7. https://www.linkedin.com/pulse/how-fix-missing-appmodulets-file-latest-version-angular-sofia-nayak-
er0df
8. https://www.youtube.com/watch?v=k9GjQenY7xw
9. https://www.telerik.com/blogs/best-both-angular-worlds-standalone-modules-combined

You might also like