HTML CSS
JavaScript
Node JS
Express JS
MongoDB Mongoose
React JS
Mongoose
FEATURES:
User can create their account
user can log into their account with username and password
A user can create Notes
A user can update Notes
A user can delete Notes
A user can view Notes
Notes will be saved within the database.
NoteCraft:
Github link:
https://github.com/maniksharma17/NoteCraft-MERN-note-taking-website
Database
I used MongoDB for database service. I imported mongoose library for an easy MongoDB connection. With mongoose, I defined two schemas and created models with them:
const noteSchema = mongoose.Schema({ title: { type: String, require: true }, description: { type: String }, color: String }) const userSchema = mongoose.Schema({ username: { type: String, require: true, unique: true }, password: { type: String, require: true }, noteList: [{ type: mongoose.Schema.Types.ObjectId, ref: "Note" }] }) const Note = mongoose.model("Note", noteSchema) const User = mongoose.model("User", userSchema)
Routing
I used express for routing through different HTTP methods. I created 5 routes. You can go through the codebase in the Github repository.
POST /signup
Here, I received user input (username and password) through req.body and with a schema defined with zod, I validated the user input. Then with a database call, I checked if the username already exist in the database. If it existed in the database I returned a message if it doesn't, then I used User.create() to add the user to the database.
router.post("/signup", async (req, res)=>{ const userPayload = req.body const parsedUser = userSchema.safeParse(userPayload) if (!parsedUser.success){ res.json({message: "Enter Username / Password must be atleast 5 characters"}) return } const existingUser = await User.findOne({ username: userPayload.username }) if (existingUser){ res.json({message: "Username exists."}) } else { User.create({ username: userPayload.username, password: userPayload.password, }).then(()=>{ res.json({message: "User created.", user: parsedUser}) }) } })
POST /signin
Here I received user input through body and checked if the user load existed in the database. If it didn't then the user would see an error message. If it did exist in the database. A JWT token is generated to be sent with other network request as a header.
router.post("/signin", async (req, res)=>{ const userPayload = req.body const existingUser = await User.findOne({ username: userPayload.username, password: userPayload.password}) if (existingUser == null){ res.json({message: "Incorrect username / passoword", auth: false}) } else { const token = jwt.sign({username: userPayload.username}, jsonKey) res.json({token: token, username: userPayload.username, message: "Logged in!"}) } })
POST /createTodo
Here, I received the note payload through body and the username of the user. through header. I validated the note data with Zod schema. then using a database query, I added the note to the notes model within the database. Also I added the ID of the note within the note list a of the user element.
router.post("/createNote", userMiddleware, (req, res)=>{ const notePayLoad = req.body const username = req.headers.username const parsedNote = createSchema.safeParse(notePayLoad) if (!parsedNote.success){ res.json({message: "Invalid data", error: parsedNote.error.issues[0].message}) return } Note.create({ title: notePayLoad.title, description: notePayLoad.description, color: notePayLoad.color }).then((response)=>{ res.json({message: "Note created"}) User.updateOne({username: username}, { "$push": { noteList: response._id } }).then() }) })
POST /completeNote
Here, I received the note payload through body and username through header. I validated the note ID using Zod schema. firstly I deleted the note from the notes collection using a database call and I also removed the ID from the array within the user element.
router.put("/completeNote", userMiddleware, async (req, res)=>{ const notePayLoad = req.body const username = req.headers.username const parsedNote = completedSchema.safeParse(notePayLoad) if (!parsedNote.success){ res.json({message: "Invalid data", error: parsedNote.error}) return } Note.deleteOne({_id: notePayLoad.id}).then(()=>{ res.json({message: "Note removed"}) }) User.updateOne({username: username}, { "$pull": { noteList: notePayLoad.id } }).then() })
GET /showNotes
Here I received username through the header. I made a database call searching for the user that username and stored it in a identifier called user. Then I returned all notes in json format whose IDs were present in the noteList array of the user element.
router.get("/getNotes", userMiddleware, async (req, res)=>{ const username = req.headers.username const user = await User.findOne({username: username}) Note.find({ _id : { "$in": user.noteList } }) .then((Notes)=>{ res.json({Notes}) }) .catch((e)=>{ res.json({message: "No note"}) }) })
Authentication and Authorization
For authentication, I used JWT (jsonwebtoken) and for Authorization, I imported the Zod library. A jwt token is generated every time a user signs into their account. That token is sent as a header with every network request for verification of the user. I created a middleware in express for verifying the jwt token.
function userMiddleware(req, res, next){ const token = req.headers.authorization try { const verifyToken = jwt.verify(token, jsonKey) if (verifyToken){ next() } } catch(e){ res.json({error: "Invalid token"}) } }
I used zod library to validate user inputs. I defined three schemas:
const createSchema = z.object({ title: z.string(), description: z.string(), color: z.string() }) const completedSchema = z.object({ id: z.string() }) const userSchema = z.object({ username: z.string().min(1), password: z.string().min(5) })
Frontend
For front end, I used react js for developing an interactive user interface. I created four components: Navbar, Signin modal, Signup modal, CreateNote, ShowNotes.
return <div className="layout"> <Navbar changeSignupState={changeSignupState} changeSigninState={changeSigninState} navbarMessage={navbarMessage}></Navbar> <div className='sectionOne'> <Signup status={signup} changeSignupState={changeSignupState} changeMessage={changeMessage}> </Signup> <Signin changeAuth = {changeAuth} status={signin} changeSigninState={changeSigninState} changeMessage={changeMessage} changeNotesRequest={changeNotesRequest}> </Signin> </div> <div className='sectionTwo'> <CreateNote changeNoteCount={changeNoteCount}> </CreateNote> <ShowNotes Notes={Notes} changeNoteCount={changeNoteCount}> </ShowNotes> </div> </div>
Conclusion
In wrapping up, Notecraft represents my humble journey into the world of web development using the MERN stack. As a beginner, creating this simple notetaking app has been both a learning experience and a source of accomplishment. Though basic in its functionality, Notecraft showcases the potential of combining MongoDB, Express.js, React, and Node.js to build something practical and user-friendly. I hope this project inspires fellow beginners to take their first steps in web development and discover the joy of bringing ideas to life through code. Happy coding!
评论
发表评论