0% found this document useful (0 votes)
14 views16 pages

MEAN Full Stack App with Angular 19

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)
14 views16 pages

MEAN Full Stack App with Angular 19

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

Creating a Full-Stack Application with Angular 19,

Express, [Link], 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 [Link] installed:

node -v

If [Link] 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 [Link] file


echo "packages:
- 'apps/*'
- 'packages/*'" > [Link]

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/
├── [Link]
└── [Link]

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 ([Link] and MongoDB)


Let's start with setting up the backend using [Link] and MongoDB.

Initialize the Server Project


1. Navigate to the server directory:

cd apps/server

2. Initialize a new [Link] file:

pnpm init

3. Open the generated [Link] and modify the main field to "src/[Link]" [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 [Link] file:

npx tsc --init

2. Update the [Link] 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/[Link]:

import express from 'express';


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

// Load environment variables


[Link]();

// Initialize Express app


const app = express();
const PORT = [Link] || 3000;

// Middleware
[Link](cors());
[Link]([Link]());

// MongoDB Connection
const MONGODB_URI = [Link].MONGODB_URI || 'mongodb://localhost:27017/meanapp';

[Link](MONGODB_URI)
.then(() => [Link]('Connected to MongoDB'))
.catch(err => [Link]('MongoDB connection error:', err));

// Basic route
[Link]('/api', (req, res) => {
[Link]({ message: 'Welcome to the MEAN Stack API' });
});

// Start server
[Link](PORT, () => {
[Link](`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/[Link]):

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: [Link] }
});

export default [Link]<IItem>('Item', ItemSchema);

3. Create a routes directory:

mkdir src/routes

4. Create routes for CRUD operations (e.g., src/routes/[Link]):

import express, { Router } from 'express';


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

const router: Router = [Link]();

// Get all items


[Link]('/', async (req, res) => {
try {
const items = await [Link]();
[Link](items);
} catch (err) {
[Link](500).json({ message: 'Server error' });
}
});

// Create a new item


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

export default router;

5. Update your src/[Link] to include the routes:

// ... other imports


import itemRoutes from './routes/items';

// ... middleware setup

// Routes
[Link]('/api/items', itemRoutes);

// ... server startup

6. Add npm scripts to [Link]:

"scripts": {
"dev": "nodemon src/[Link]",
"build": "tsc",
"start": "node dist/[Link]"
}

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 [Link] file in the client directory:

{
"/api": {
"target": "[Link]
"secure": false,
"logLevel": "debug"
}
}

Update the [Link] file to use the proxy configuration:

"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"proxyConfig": "[Link]"
},
// ... 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/[Link]):


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 [Link]<Item[]>([Link]);
}

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


return [Link]<Item>([Link], 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/[Link]):

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


import { ItemService, Item } from '../../services/[Link]';
import { CommonModule } from '@angular/common';

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

constructor(private itemService: ItemService) { }


ngOnInit(): void {
[Link]();
}

fetchItems(): void {
[Link]().subscribe({
next: (data) => {
[Link] = data;
[Link] = false;
},
error: (err) => {
[Link] = 'Failed to load items';
[Link] = false;
[Link](err);
}
});
}
}

3. Update the template file (src/app/components/item-list/[Link]):

<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="[Link] === 0">No items found.</div>

<div *ngIf="[Link] > 0" class="items-grid">


<div *ngFor="let item of items" class="item-card">
<h3>{{ [Link] }}</h3>
<p>{{ [Link] }}</p>
<small>Created: {{ [Link] | date }}</small>
</div>
</div>
</div>
</div>

4. Add some basic styling (src/app/components/item-list/[Link]):

.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/[Link] to include HttpClientModule:

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


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

import { routes } from './[Link]';

export const appConfig: ApplicationConfig = {


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

Update the App Routes


Update src/app/[Link] to include the ItemListComponent:

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


import { ItemListComponent } from './components/item-list/[Link]';

export const routes: Routes = [


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

Update App Component


Update src/app/[Link]:

<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/[Link]):

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


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

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

constructor(
private fb: FormBuilder,
private itemService: ItemService
) {
[Link] = [Link]({
name: ['', [[Link], [Link](3)]],
description: ['', [[Link], [Link](5)]]
});
}

onSubmit(): void {
if ([Link]) {
return;
}
[Link] = true;
[Link] = null;
[Link] = false;

[Link]([Link]).subscribe({
next: () => {
[Link] = false;
[Link] = true;
[Link]();
},
error: (err) => {
[Link] = false;
[Link] = 'Failed to add item';
[Link](err);
}
});
}
}

3. Create the template (src/app/components/item-form/[Link]):

<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="[Link]('name')?.invalid && [Link]('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="[Link]('description')?.invalid && [Link]('description')?.to
Description is required and must be at least 5 characters long.
</div>
</div>

<button type="submit" [disabled]="[Link] || 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/[Link]):

.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/[Link] to include the form component:


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

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/[Link]) 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 [Link] 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 [Link] backend server and the Angular frontend development
server.
3. Open your browser and navigate to:
Frontend: [Link]
Backend API: [Link]

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 [Link]/[Link] 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 [Link] in


Modern Angular Projects
If you've noticed that the [Link] 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 [Link] 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 [Link] 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
[Link] [7] [8] .

Option 3: Manually Add [Link] to Your Existing Project


If you need to add an [Link] file to your existing standalone project:
1. Create a new file named [Link] 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 './[Link]';

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

3. Update your [Link] 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 [Link] 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 [Link], 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. [Link]
2. [Link]
3. [Link]
4. [Link]
5. [Link]
6. [Link]
7. [Link]
er0df
8. [Link]
9. [Link]

Common questions

Powered by AI

The Angular team has shifted towards standalone components to simplify development by reducing boilerplate code associated with NgModules. This transition eliminates the need for an app.module.ts file by allowing direct importation of dependencies within individual components. The change aims to provide better tree-shaking, resulting in smaller bundle sizes and more portable components. Standalone components are now the default configuration in Angular CLI as of Angular v17 .

To set up a basic Express server, first, install necessary dependencies such as express, mongoose, and dotenv. Initialize the Express app and use middleware like cors and express.json for handling requests. Configure MongoDB connection using mongoose.connect with a URI and handle connection success or error messages. Define basic routes using app.get to respond to requests. Set the Express app to listen on a designated port, enabling it to handle incoming API requests .

Setting up a monorepo structure for a full-stack application involves several key steps. First, create a root directory for the project, then initialize pnpm in the root directory and create a pnpm-workspace.yaml file. This file defines the packages scope, such as 'apps/*' for application code and 'packages/*' for shared code. Next, scaffold the directory structure to separate the frontend and backend code (e.g., creating apps/client and apps/server directories). This approach is advantageous because it allows managing both the frontend and backend code within a single repository, facilitating shared configurations and dependencies .

A monorepo structure enhances maintainability by centralizing both frontend and backend code within a single repository, enabling shared configurations and dependencies, thus reducing duplication. This setup fosters a consistent development workflow, simplifies code reuse, and facilitates coordinated versioning and deployments. Having a unified repository helps in understanding and modifying interconnected parts of the application without navigating multiple repositories .

Environment variables play a critical role in server-side configuration by allowing sensitive information such as database URIs and server ports to be stored outside the source code, enhancing security and flexibility. Using libraries like dotenv, these variables can be easily loaded into the application at runtime without hardcoding them in the source, permitting different configurations for different environments (e.g., development, testing, production).

Angular's standalone component approach potentially improves performance by reducing the overhead associated with NgModules. By eliminating the need for every component to be declared in a module, it allows for more direct and fine-grained dependency management. This reduces the amount of bootstrap code and improves tree-shaking, which means only the necessary code is bundled and shipped, resulting in smaller application sizes and possibly faster execution times .

Setting up Angular to handle forms involves creating a Reactive Form using FormBuilder to define form controls with Validators for input validation. Validators such as Validators.required and Validators.minLength enforce conditions on inputs to ensure data integrity. Consider providing user feedback through error messages for invalid controls?• for better UX?• and integrating the form with services that communicate with the backend API for data submission. Additionally, designing forms to handle asynchronous submissions and error handling enhances robustness .

pnpm offers several benefits over npm when used in a MEAN stack application. It is designed for efficient disk space usage and faster installations by storing a single version of a package in a central location, which is then symlinked into projects. This deduplication reduces overhead and improves overall performance. pnpm's strictness in dependencies helps catch potential conflicts early, leading to a more stable dependency tree. These factors enhance the speed and efficiency of both installation and runtime, especially in complex projects such as those utilizing the monorepo structure .

The item service uses Angular's HttpClient to communicate with the backend API. It defines methods such as getItems() to perform HTTP GET requests and addItem() for HTTP POST requests on the '/api/items' endpoint. Observable streams are used to handle the asynchronous nature of these HTTP requests, which allows for subscription to the response data or errors .

Using pnpm with a monorepo offers several advantages, such as reduced disk space consumption and faster installations due to its deduplication strategy. This combination streamlines the management of shared dependencies across multiple applications within the monorepo, ensuring consistency and reducing conflicts. However, potential drawbacks include the need for developers to become familiar with pnpm-specific commands and configurations, which may differ from npm, and potential issues with existing scripts and tooling that assume npm usage. Developers must weigh these pros and cons based on team familiarity and project requirements .

You might also like