Notes Maker App using MEAN Stack
Last Updated :
23 Jul, 2025
The Notes Maker project is a helpful web application designed to help users effectively create, manage, and organize their notes. In this article we are utilizing the MEAN (MongoDB, Express, Angular, Node) Stack, to build a notes maker application that provides a seamless user experience for note-taking.
Final OutputPrerequisites:
Functionalities of Notes Maker App
The project employs a backend built with Node.js and Express.js, utilizing MongoDB as the database for note storage. On the front end, Angular.js is employed to create an interactive user interface. The functionalities include:
- Adding new notes with titles and content.
- Displaying a list of existing notes.
- Update/Delete Notes.
Steps to Create the Project
Step 1: Create a new directory for your project and navigate to it using the terminal.
mkdir NOTEAPP
cd NOTEAPP
Step 2: Create a server directory for your backend and navigate into it.
mkdir server
cd server
Step 3: Initialize a new Node.js project for the backend.
npm init -y
Step 4: Install the necessary backend dependencies.
npm install express mongoose cors
Project Structure (Backend):
Backed StructureDependencies (Backend):
"dependencies": {
"cors": "^2.8.5",
"express": "^4.19.2",
"mongoose": "^8.4.0"
}
Step 5: Create a server.js file inside the server directory and add the following code:
JavaScript
// server.js
const express = require("express");
const mongoose = require("mongoose");
const noteRoutes = require("./routes/noteRoutes");
const cors = require("cors");
const app = express();
const port = 5000;
// Connect to MongoDB
mongoose
.connect("mongodb://localhost:27017/note-app")
.then(() => {
console.log("Connected successfully to MongoDB");
})
.catch((err) => {
console.error("Error occurred while connecting to MongoDB:", err);
});
// Middleware
app.use(express.json());
app.use(cors());
app.use(express.urlencoded({ extended: true }));
app.use("/notes", noteRoutes);
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
JavaScript
// model/note.js
const mongoose = require("mongoose");
const noteSchema = new mongoose.Schema({
title: { type: String, },
description: { type: String, },
});
module.exports = mongoose.model("Note", noteSchema);
JavaScript
// routes/noteRoutes.js
const express = require("express");
const router = express.Router();
const Note = require("../model/note");
const note = require("../model/note");
// Get all notes
router.get("/", async (req, res) => {
try {
const notes = await Note.find();
res.json(notes);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
// Create a note
router.post("/", async (req, res) => {
console.log(req.body);
const note = new Note({
title: req.body.title,
description: req.body.description,
});
try {
const newNote = await note.save();
res.status(201).json(newNote);
} catch (err) {
res.status(400).json({ message: err.message });
}
});
// Update a note
router.put("/:id", async (req, res) => {
console.log(req.params.id);
try {
const note = await Note.findById(req.params.id);
if (note == null) {
return res.status(404).json({ message: "Note not found" });
}
if (req.body.title != null) {
note.title = req.body.title;
}
if (req.body.description != null) {
note.description = req.body.description;
}
const updatedNote = await note.save();
res.json(updatedNote);
} catch (err) {
res.status(400).json({ message: err.message });
}
});
// Delete a note
router.delete("/:id", async (req, res) => {
try {
const deletedNote = await Note.findByIdAndDelete(req.params.id);
const notes = await Note.find();
res.json(notes);
} catch (err) {
res.status(500).json({ message: err.message });
}
});
module.exports = router;
To run the backend server run the following command.
node server.js
Step 6: Navigate to the root directory. Run the following command to initialize a new Angular app:
npm install -g @angular/cli
cd client
ng new note-maker-app
Folder Structure(Frontend):
Front End StructureDependencies(Frontend):
"dependencies": {
"@angular/animations": "^17.3.0",
"@angular/common": "^17.3.0",
"@angular/compiler": "^17.3.0",
"@angular/core": "^17.3.0",
"@angular/forms": "^17.3.0",
"@angular/platform-browser": "^17.3.0",
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
}
Example: Add the given code in the respective files.
HTML
<!-- /src/app/app.component.html -->
<div class="note-form">
<h2>Note Maker App</h2>
<form (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="title">Title:</label>
<input
type="text"
id="title"
placeholder="Enter Note Title..."
[(ngModel)]="title"
name="title"
required
/>
</div>
<div class="form-group">
<label for="location">Description:</label>
<input
type="text"
id="location"
placeholder="Enter Description..."
[(ngModel)]="description"
name="description"
required
/>
</div>
<button type="submit">Submit</button>
</form>
<div class="existing-notes">
<h2>Existing Notes</h2>
<ul>
<li *ngFor="let note of notes">
<div class="notes-details">
<strong>Title:</strong> {{ note.title }},
<strong>Description</strong> {{ note.description }},
</div>
<div class="notes-actions">
<button class="editbtn" (click)="editNote(note)">Edit</button>
<button class="deletebtn" (click)="deleteNote(note._id)">Delete</button>
</div>
</li>
</ul>
</div>
</div>
CSS
/* app.component.css */
body {
font-family: 'Roboto', sans-serif;
background: linear-gradient(to right, #ff6e7f, #bfe9ff);
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-attachment: fixed;
}
/* Container */
.note-form {
background-color: #ffffff;
margin: 0 auto;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
width: 500px;
text-align: left;
border-top: 5px solid #d9534f;
position: relative;
overflow: hidden;
background-image: url('https://example.com/disaster-background.jpg');
background-size: cover;
background-position: center;
background-blend-mode: overlay;
}
.emergency-form::before {
content: '';
background: rgba(255, 255, 255, 0.8);
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 0;
}
.emergency-form > * {
position: relative;
z-index: 1;
}
/* Headings */
h2 {
color: green;
margin-top: 0;
border-bottom: 2px solid green;
padding-bottom: 10px;
font-size: 24px;
}
/* Paragraph */
p {
color: #555;
margin: 10px 0;
font-size: 16px;
}
/* Form Styles */
form {
margin-top: 20px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
color: #333;
font-weight: bold;
margin-bottom: 5px;
}
.form-group input {
width: 100%;
padding: 12px;
border: 1px solid #ccc;
border-radius: 5px;
box-sizing: border-box;
font-size: 16px;
}
/* Button Styles */
button {
width: 100%;
padding: 15px;
background-color:green;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 18px;
font-weight: bold;
margin-top: 20px;
transition: background-color 0.3s, transform 0.3s;
}
button:hover {
transform: scale(1.05);
}
.editbtn{
width: 50%;
margin: 1px;
background-color:green;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 18px;
font-weight: bold;
margin-top: 20px;
transition: background-color 0.3s, transform 0.3s;
}
.deletebtn{
width: 50%;
margin: 1px;
background-color:red;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 18px;
font-weight: bold;
margin-top: 20px;
transition: background-color 0.3s, transform 0.3s;
}
/* Existing Emergencies Section */
.existing-notes {
margin-top: 40px;
}
.existing-notes h2 {
color: #d9534f;
}
.existing-notes ul {
list-style: none;
padding: 0;
}
.existing-notes li {
background-color: #f9f9f9;
border: 1px solid #ddd;
padding: 15px;
margin-bottom: 15px;
border-radius: 5px;
position: relative;
display: flex;
justify-content: space-between; /* Aligns content and actions side by side */
align-items: center;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.existing-notes li:hover {
transform: translateY(-5px);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}
.existing-notes li::before {
content: '?';
position: absolute;
left: 10px;
top: 10px;
font-size: 24px;
color: #0e4b07;
}
.existing-notes li strong {
color: #333;
display: block;
}
/* Media Queries for Responsive Design */
@media (max-width: 600px) {
.emergency-form {
width: 100%;
padding: 20px;
}
.form-group input,
button {
padding: 10px;
}
button {
font-size: 16px;
}
}
JavaScript
// /src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { HttpClient, HttpClientModule } from '@angular/common/http'; // Import HttpClientModule
import { FormsModule } from '@angular/forms'; // Import FormsModule for ngModel
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
imports: [
CommonModule,
FormsModule, // Add FormsModule here
HttpClientModule // Add HttpClientModule here
],
standalone: true
})
export class AppComponent implements OnInit {
// Form inputs
title = '';
description = '';
notes: any[] = [];
constructor(private http: HttpClient) { }
ngOnInit(): void {
this.fetchNotes();
}
fetchNotes(): void {
this.http.get<any[]>('http://localhost:5000/notes').subscribe(
(data) => {
this.notes = data;
},
(error) => {
console.error('Error fetching notes:', error);
}
);
}
// Handle form submission
onSubmit(): void {
if (this.title.trim() === '' || this.description.trim() === '') {
alert('Please fill in all fields!');
return;
}
const newNote = {
title: this.title,
description: this.description,
};
this.http.post('http://localhost:5000/notes', newNote).subscribe(
(data) => {
console.log('Data submitted:', data);
// Reset form inputs
this.title = '';
this.description = '';
this.fetchNotes();
},
(error) => {
console.error('Error submitting data:', error);
}
);
}
editNote(note: any): void {
console.log(note)
const newTitle = prompt('Enter new title:', note.title);
const newDescription = prompt('Enter new description:', note.description);
if (newTitle !== null && newDescription !== null) {
const updatedNotes = { ...note, title: newTitle, description: newDescription };
this.http.put(`http://localhost:5000/notes/${note._id}`, updatedNotes).subscribe(
(data) => {
console.log('Updated note:', data);
this.fetchNotes(); // Refresh the list
},
(error) => {
console.error('Error updating notes:', error);
}
);
}
}
deleteNote(id: number): void {
this.http.delete<any[]>('http://localhost:5000/notes/' + id).subscribe(
(updatedNotes) => {
this.notes = updatedNotes;
},
(error) => {
console.error('Error deleting notes:', error);
}
);
}
}
JavaScript
//main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent, appConfig)
.catch((err) => console.error(err));
To run the application, type the following command:
ng serve
Output:
Notes Maker app using MEAN stack
Similar Reads
How To
Difference Between
What is the difference between Lua and Luau?Lua and Luau are two scripting languages that share a similar foundation but are designed for different purposes. Lua is a lightweight, general-purpose language often used in game development and embedded systems. Luau, on the other hand, is a modified version of Lua created specifically for Roblox
5 min read
DeepSeek R1 vs V3: A Head-to-Head Comparison of Two AI ModelsTechnology keeps evolving, as does the AI landscape. DeepSeekâs latest models, DeepSeek V3 and DeepSeek R1 RL, are at the forefront of this revolution. While both models share a foundation in the Mixture of Experts (MoE) architecture, their design philosophies, capabilities, and applications diverge
7 min read
DeepSeek vs ChatGPT: Comparison of Best AI Titans in 2025Not long ago, I had my first experience with ChatGPT version 3.5, and I was instantly amazed. It wasnât just the speed with which it tackled problems but also how naturally it mimicked human conversation. That moment was like the start of a big AI chatbot competition, with ChatGPT leading the charge
11 min read
Google Calendar vs Outlook Calendar (2025 Guide): Which is Best?Managing schedules efficiently often boils down to selecting the right calendar tool. Comparing Google Calendar vs Outlook Calendar can help users identify the platform best suited to their personal or professional needs. With Google Calendar offering simplicity and effective Google Calendar integra
6 min read
Error Fixing
How to Fix ChatGPT "Unable to Load Conversation" Error (Instant Fix)The âUnable to Load Conversationâ error in ChatGPT can be annoying, especially if you're trying to access an important chat or reference a previous interaction with ChatGPT. This issue can occur due to various reasons, including server-side problems, browser issues, or network connectivity glitches.
7 min read
How to fix "ChatGPT can't access external files or links"The "ChatGPT can't access external files or links" issue occurs when the AI cannot interact with documents, URLs, or external resources due to technical limitations, configuration errors, or policy restrictions. Below is a step-by-step guide to resolve this problem and enable ChatGPT to work with ex
3 min read
Quick Fixes for Your Deactivated ChatGPT Account: Step-by-Step GuideIf you've encountered the "Workspace has been deactivated" message in ChatGPT, it means that your workspace has been temporarily suspended or disabled. Many users encounter this issue for various reasons, whether it's due to policy violations, inactivity, or technical glitches. Fortunately, there ar
7 min read
How to Fix Insufficient Balance Error When Using DeepseekGetting an "Insufficient Balance" error in DeepSeek can be annoying, especially when youâre trying to generate images, process AI tasks, or use DeepSeek premium features. This issue typically occurs when your account credits or subscription balance has run out, preventing you from accessing certain
8 min read
How to Fix DeepSeek API Error 401 "Authentication Fails"Encountering a 401 Unauthorized error while using the DeepSeek API? Donât worryâyouâre not alone! This error typically occurs when thereâs an issue with authentication, such as missing or incorrect API keys, expired tokens, or improper request headers.In this guide, weâll walk you through the common
8 min read
How to Fix HTTP Request DeepSeek R1 Issue [Quick Fixes]The HTTP Request DeepSeek R1 issue occurs when the system fails to send or receive data due to network, configuration, or server-related problems. This guide provides easy-to-follow, in-depth solutions to resolve how to fix HTTP requests in DeepSeek R1 and ensure smooth communication with DeepSeek R
6 min read
How to Fix 500 Internal Server Error When Loading DeepSeekEncountering a "500 Internal Server Error" while accessing DeepSeek can be both frustrating and disruptive, especially when you're in the middle of important tasks. This error indicates a problem on the server side, preventing your request from being processed. In this guide, we'll explore the commo
4 min read