Notes Maker App using MERN Stack
The “Notes Maker” project is an helpful web application designed to help users effectively create, manage, and organize their notes. In this article we are utilizing the MERN (MongoDB, Express, React, Node) Stack, to build a notes maker application that provides a seamless user experience for note-taking.
Preview of final output: Let us have a look at how the final application will look like.
Prerequisites
Approach to create Notes Maker App:
- The Notes Maker project is approached with a structured methodology, dividing responsibilities into stateful backend controllers and stateless frontend components.
- On the backend, Express.js connects to MongoDB Atlas, with server.js acting as the controller for CRUD operations.
- The frontend uses React.js, where App.js manages overall state and communication with the backend, while stateless components like AddNote.js and NoteList.js handle UI elements.
- A minimal directory structure ensures clarity, and CSS styling enhances the user interface.
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, React.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 notes-maker
cd notes-maker
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 body-parser
Project Structure (Backend):
Dependencies (Backend):
"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.0.3"
}
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 cors = require( "cors" ); const bodyParser = require( "body-parser" ); const app = express(); const PORT = process.env.PORT || 5000; // Middleware app.use(cors()); app.use(bodyParser.json()); // Connect to MongoDB Atlas const dbURI = "your mongodb connection string" ; mongoose.connect(dbURI, { useNewUrlParser: true , useUnifiedTopology: true }); // Define Note model const Note = mongoose.model( "Note" , { title: String, content: String, }); // Listen for successful MongoDB connection mongoose.connection.on( "connected" , () => { console.log( "Connected to MongoDB Atlas" ); }); // Listen for MongoDB connection errors mongoose.connection.on( "error" , (err) => { console.error( "MongoDB connection error:" , err); }); // Routes app.get( "/" , (req, res) => { res.send( "Hello, this is the root!" ); }); app.get( "/api/notes" , async (req, res) => { try { const notes = await Note.find(); res.json(notes); } catch (error) { res.status(500).json({ message: error.message }); } }); // Update Note by ID // Update Note by ID app.put( "/api/notes/:id" , async (req, res) => { const { title, content } = req.body; const noteId = req.params.id; try { const updatedNote = await Note.findByIdAndUpdate( noteId, { title, content }, { new : true } ); res.json(updatedNote); } catch (error) { res.status(404).json({ message: "Note not found" }); } }); // Delete Note by ID app. delete ( "/api/notes/:id" , async (req, res) => { const noteId = req.params.id; try { await Note.findByIdAndDelete(noteId); res.json({ message: "Note deleted successfully" }); } catch (error) { res.status(404).json({ message: "Note not found" }); } }); app.post( "/api/notes" , async (req, res) => { const { title, content } = req.body; const note = new Note({ title, content }); try { const newNote = await note.save(); res.status(201).json(newNote); } catch (error) { res.status(400).json({ message: error.message }); } }); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); }); |
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 React app:
npx create-react-app client
cd client
Step 7: Install the required dependencies.
npm i axios
Folder Structure(Frontend):
Dependencies(Frontend):
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
Example: Add the given code in the respective files.
Javascript
//App.js import React, { useState, useEffect } from "react" ; import axios from "axios" ; import "./App.css" ; import NoteList from "./components/NoteList" ; import AddNote from "./components/AddNote" ; const App = () => { const [notes, setNotes] = useState([]); const [title, setTitle] = useState( "" ); const [content, setContent] = useState( "" ); useEffect(() => { // Fetch notes from the server axios .get( "http://localhost:5000/api/notes" ) .then((response) => setNotes(response.data)) . catch ((error) => console.error( "Error fetching notes:" , error)); }, []); const handleAddNote = () => { // Add a new note to the server axios .post( "http://localhost:5000/api/notes" , { title, content }) .then((response) => { setNotes([...notes, response.data]); setTitle( "" ); setContent( "" ); }) . catch ((error) => console.error( "Error adding note:" , error)); }; const handleEditNote = (id, updatedTitle, updatedContent) => { // Update note by ID axios .put(`http: //localhost:5000/api/notes/${id}`, { title: updatedTitle, content: updatedContent, }) .then((response) => { const updatedNotes = notes.map((note) => note._id === id ? response.data : note ); setNotes(updatedNotes); }) . catch ((error) => console.error( "Error updating note:" , error)); }; const handleDeleteNote = (id) => { // Delete note by ID axios . delete (`http: //localhost:5000/api/notes/${id}`) .then((response) => { const updatedNotes = notes.filter((note) => note._id !== id); setNotes(updatedNotes); }) . catch ((error) => console.error( "Error deleting note:" , error)); }; return ( <div className= "app-container" > <h1>Notes App</h1> <AddNote title={title} setTitle={setTitle} content={content} setContent={setContent} onAddNote={handleAddNote} /> <NoteList notes={notes} onEditNote={handleEditNote} onDeleteNote={handleDeleteNote} /> </div> ); }; export default App; |
Javascript
// components/NoteList.js import React from "react" ; const NoteList = ({ notes, onEditNote, onDeleteNote }) => { return ( <ul> {notes.map((note) => ( <li key={note._id}> <strong>{note.title}</strong> <p>{note.content}</p> <button className= "button2" style={{ marginRight: "15px" }} onClick={() => onEditNote( note._id, prompt( "Enter updated title:" , note.title), prompt( "Enter updated content:" , note.content) ) } > Edit </button> <button className= "button2" onClick={() => onDeleteNote(note._id)} > Delete </button> </li> ))} </ul> ); }; export default NoteList; |
Javascript
//componenets/AddNode.js import React from "react" ; const AddNote = ({ title, setTitle, content, setContent, onAddNote }) => { return ( <div> <h2>Add Note</h2> <input type= "text" placeholder= "Title" value={title} onChange={(e) => setTitle(e.target.value)} /> <textarea placeholder= "Content" value={content} onChange={(e) => setContent(e.target.value)} ></textarea> <button className= "button1" onClick={onAddNote}> Add Note </button> </div> ); }; export default AddNote; |
CSS
/* App.css */ body { font-family : 'Arial' , sans-serif ; background-color : #f4f4f4 ; margin : 0 ; padding : 0 ; } .app-container { max-width : 800px ; margin : 20px auto ; padding : 20px ; background-color : #fff ; box-shadow: 0 0 10px rgba( 0 , 0 , 0 , 0.1 ); border-radius: 5px ; } h 1 { color : #333 ; } form { display : flex; flex- direction : column; } label { margin-bottom : 8px ; font-size : 16px ; } input { margin-bottom : 15px ; padding : 10px ; font-size : 16px ; border : 1px solid #ccc ; border-radius: 5px ; box-sizing: border-box; width : 100% ; } .button 1 , .button 2 { margin-bottom : 15px ; padding : 10px ; font-size : 16px ; border : 1px solid #ccc ; border-radius: 5px ; box-sizing: border-box; } .button 1 { width : 100% ; } .button 2 { width : 49% ; } textarea { margin-bottom : 15px ; padding : 10px ; font-size : 16px ; border : 1px solid #ccc ; border-radius: 5px ; box-sizing: border-box; resize: vertical; height : 100px ; width : 100% ; } button { background-color : #4caf50 ; color : #fff ; cursor : pointer ; } button:hover { background-color : #45a049 ; } ul { list-style-type : none ; padding : 0 ; } li { margin-bottom : 15px ; padding : 10px ; background-color : #eee ; border-radius: 5px ; } li strong { font-size : 18px ; color : #333 ; } li p { font-size : 16px ; color : #555 ; } |
To run the application,type the following command:
node server.js
Output: