From 81e7cd6bc6a62e372fe7a0e3ed1eee53ada86ddb Mon Sep 17 00:00:00 2001 From: fridascript Date: Thu, 22 Jan 2026 11:31:46 +0100 Subject: [PATCH 1/7] adds GET endpoints --- server.js | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/server.js b/server.js index f47771b..faa57cd 100644 --- a/server.js +++ b/server.js @@ -1,5 +1,8 @@ import cors from "cors" import express from "express" +import thoughtsData from "./data.json" with { type: "json" } + +let thoughts = thoughtsData // Defines the port the app will run on. Defaults to 8080, but can be overridden // when starting the server. Example command to overwrite PORT env variable value: @@ -12,11 +15,55 @@ app.use(cors()) app.use(express.json()) // Start defining your routes here +// app.get("/", (req, res) => { +// res.send("Hello Technigo!") +// }) + app.get("/", (req, res) => { - res.send("Hello Technigo!") -}) + res.json ({ + message: "Happy Thoughts API!", + endpoints: [ + { + path: "/thoughts", + method: "GET", + description: "Returns all thoughts", + queryParams: "?sort=hearts or ?sort=date" + }, + { + path: "/thoughts/:id", + method: "GET", + description: "Returns a single thought by ID" + } + ] + }); +}); + +app.get("/thoughts", (req, res) => { + const { sort } = req.query + + let result = [...thoughts] + + if (sort === "hearts") { + result.sort((a, b) => b.hearts - a.hearts) + } else if (sort === "date") { + result.sort ((a, b) => new Date(b.createdAt) - new Date(a.createdAt)) + } + + res.json(result) +}); + +app.get("/thoughts/:id", (req,res) => { + const { id } = req.params + const thought = thoughts.find((t) => t._id === id) + + if (thought) { + res.json(thought) + }else { + res.status(404).json({error: "Thought not found"}) + } +}); // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`) -}) +}); From b5e93bf9ab20eba0bf1113c6b9aa6a04aa57b7fa Mon Sep 17 00:00:00 2001 From: fridascript Date: Wed, 28 Jan 2026 13:41:38 +0100 Subject: [PATCH 2/7] connects mongodb / mongoose --- package.json | 1 + seed-database.js | 27 +++++++++ server.js | 139 +++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 151 insertions(+), 16 deletions(-) create mode 100644 seed-database.js diff --git a/package.json b/package.json index bf25bb6..c9571b5 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@babel/preset-env": "^7.16.11", "cors": "^2.8.5", "express": "^4.17.3", + "mongoose": "^9.1.5", "nodemon": "^3.0.1" } } diff --git a/seed-database.js b/seed-database.js new file mode 100644 index 0000000..8fcd1db --- /dev/null +++ b/seed-database.js @@ -0,0 +1,27 @@ +import mongoose from "mongoose" +import thoughtsData from "./data.json" with { type: "json" } + +const mongoUrl = "mongodb://localhost/happythoughts" +mongoose.connect(mongoUrl) + +const ThoughtSchema = new mongoose.Schema({ + message: String, + hearts: Number, + createdAt: Date +}) + +const Thought = mongoose.model("Thought", ThoughtSchema) + +const seedDatabase = async () => { + try { + await Thought.deleteMany() + await Thought.insertMany(thoughtsData) + consoloe.log("Database seeded successfully!") + } catch (error) { + console.error("Error seeding database:", error) + } finally { + mongoose.connection.close() + } +} + +seedDatabase() \ No newline at end of file diff --git a/server.js b/server.js index faa57cd..cd06094 100644 --- a/server.js +++ b/server.js @@ -1,6 +1,33 @@ -import cors from "cors" -import express from "express" -import thoughtsData from "./data.json" with { type: "json" } +import cors from "cors"; +import express from "express"; +import thoughtsData from "./data.json" with { type: "json" }; +import mongoose from "mongoose"; + + +//connect to MongoDB +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/happythoughts" +mongoose.connect(mongoUrl) +mongoose.Promise = Promise + +// define the thought model + +const ThoughtSchema = new mongoose.Schema ({ + message: { + type: String, + required: true, + minglength: 5, + maxlength: 140 + }, + hearts: { + type: Number, + default: 0 + }, + createdAt: { + type: Date, + default: Date.now + } +}) +const Thought = mongoose.model("Thought", ThoughtSchema) let thoughts = thoughtsData @@ -14,12 +41,20 @@ const app = express() app.use(cors()) app.use(express.json()) -// Start defining your routes here -// app.get("/", (req, res) => { -// res.send("Hello Technigo!") +//ex from class +// const Animal =mongoose.model('Animal', { +// name: string, +// age: number, +// isFurry: Boolean // }) +// new Animal ({ name:'Alfons', age: 2, isFurry: true }).save() +// new Animal ({ name:'Pelle', age: 6, isFurry: false }).save() + +// Start defining your routes here + app.get("/", (req, res) => { + res.json ({ message: "Happy Thoughts API!", endpoints: [ @@ -33,33 +68,105 @@ app.get("/", (req, res) => { path: "/thoughts/:id", method: "GET", description: "Returns a single thought by ID" + }, + { + path: "/thoughts", + method: "POST", + description: "Creates a new thought", + body:{ message: "Your happy thought (5-140 characters)" } + } ] - }); + }) }); -app.get("/thoughts", (req, res) => { +app.get("/thoughts", async (req, res) => { + try{ const { sort } = req.query - - let result = [...thoughts] + let query = Thought.find() if (sort === "hearts") { - result.sort((a, b) => b.hearts - a.hearts) + query = query.sort({ hearts: -1 }) } else if (sort === "date") { - result.sort ((a, b) => new Date(b.createdAt) - new Date(a.createdAt)) + query.sort = query.sort ({ createdAt: -1}) } - res.json(result) + const thoughts = await query.limit (25) + res.json(thoughts) +} catch (error) { + res.status(400).json({ error: "Sorry! Couldn't fetch any thoughts"}) +} }); -app.get("/thoughts/:id", (req,res) => { +app.get("/thoughts/:id", async (req,res) => { + try { const { id } = req.params - const thought = thoughts.find((t) => t._id === id) + const thought = await Thought.findById(id) if (thought) { res.json(thought) }else { - res.status(404).json({error: "Thought not found"}) + res.status(404).json({error: "Sorry, no thought found"}) + } +} catch (error ) { + res.status(400).json({ error: "Invalid thought ID"}) +} +}); + +// route to create a new thought +app.post("/thoughts", async (req, res) => { + try{ + const { message } = req.body + + const newThought = await Thought.create ({ message }) + res.status(201).json(newThought) + + + } catch (error) { + if (error.name === "ValidationError"){ + res.status(400).json({ + error: "Validation failed", + details: error.message + }) + } else { + res.status(500).json({ error: "Could not create thought"}) + } + } +}); + +// route to delete thoughts +app.delete("/thoughts/:id", async (req, res) =>{ + try { + const { id } = req.params + const deletedThought = await Thought.findByIdAndDelete(id) + + if (deletedThought) { + res.json ({ message: "Thought deleted", deletedThought }) + } else { + res.status(404).json({ error: "Thought not found"}) + } + } catch (error) { + res.status(400).json({ error: "Could not delete thought" }) + } +}); + +// route to like a thought +app.patch("/thoughts/:id/like", async (req,res) => { + try { + const { id } = req.params + const thought = await Thought.findByIdAndUpdate ( + id, + {$inc: {hearts: 1 }}, + { new: true } + ) + + if (thought) { + res.json(thought) + } else { + res.status(404).json({ error: "Thought not found"}) + } + } catch (error) { + res.status(400).json({ error: "Could not update thought" }) } }); From b100dbeb7f09d6831257b681d264d51f3731d335 Mon Sep 17 00:00:00 2001 From: fridascript Date: Wed, 28 Jan 2026 16:33:49 +0100 Subject: [PATCH 3/7] add final CRUD push --- server.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server.js b/server.js index cd06094..4450513 100644 --- a/server.js +++ b/server.js @@ -74,7 +74,16 @@ app.get("/", (req, res) => { method: "POST", description: "Creates a new thought", body:{ message: "Your happy thought (5-140 characters)" } - + }, + { + path: "/thoughts:/id/like", + method: "PATCH", + description: "Likes a thought message" + }, + { + path: "/thoughts/:id", + method: "DELETE", + description: "Deletes a thought by ID" } ] }) From 2aaf8845bf740279a2bc8d643c019b5e2370d5b0 Mon Sep 17 00:00:00 2001 From: fridascript Date: Wed, 28 Jan 2026 17:58:25 +0100 Subject: [PATCH 4/7] adds atlas url --- package.json | 1 + seed-database.js | 14 ++++++++++++-- server.js | 30 +++++++++++------------------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index c9571b5..6c624c0 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", "cors": "^2.8.5", + "dotenv": "^17.2.3", "express": "^4.17.3", "mongoose": "^9.1.5", "nodemon": "^3.0.1" diff --git a/seed-database.js b/seed-database.js index 8fcd1db..a19230a 100644 --- a/seed-database.js +++ b/seed-database.js @@ -1,9 +1,14 @@ +import dotenv from "dotenv" +dotenv.config() import mongoose from "mongoose" import thoughtsData from "./data.json" with { type: "json" } -const mongoUrl = "mongodb://localhost/happythoughts" +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/happythoughts" mongoose.connect(mongoUrl) + + +// defining 'though schema' simple for seeding const ThoughtSchema = new mongoose.Schema({ message: String, hearts: Number, @@ -12,14 +17,19 @@ const ThoughtSchema = new mongoose.Schema({ const Thought = mongoose.model("Thought", ThoughtSchema) +// function to populate the database const seedDatabase = async () => { try { + // delete all existing thoughts await Thought.deleteMany() + // insert all thoughts from data.json await Thought.insertMany(thoughtsData) - consoloe.log("Database seeded successfully!") + console.log("Database seeded successfully!") } catch (error) { console.error("Error seeding database:", error) } finally { + + // closes the database connection when done mongoose.connection.close() } } diff --git a/server.js b/server.js index 4450513..8e64f6a 100644 --- a/server.js +++ b/server.js @@ -1,3 +1,5 @@ +import dotenv from "dotenv" +dotenv.config() import cors from "cors"; import express from "express"; import thoughtsData from "./data.json" with { type: "json" }; @@ -29,30 +31,18 @@ const ThoughtSchema = new mongoose.Schema ({ }) const Thought = mongoose.model("Thought", ThoughtSchema) -let thoughts = thoughtsData -// Defines the port the app will run on. Defaults to 8080, but can be overridden -// when starting the server. Example command to overwrite PORT env variable value: -// PORT=9000 npm start +// Defines the port the app will run on. const port = process.env.PORT || 8080 const app = express() -// Add middlewares to enable cors and json body parsing +// Middlewares to enable cors and json body parsing app.use(cors()) app.use(express.json()) -//ex from class -// const Animal =mongoose.model('Animal', { -// name: string, -// age: number, -// isFurry: Boolean -// }) - -// new Animal ({ name:'Alfons', age: 2, isFurry: true }).save() -// new Animal ({ name:'Pelle', age: 6, isFurry: false }).save() - -// Start defining your routes here +//routes +// documentation endpoints app.get("/", (req, res) => { res.json ({ @@ -73,10 +63,10 @@ app.get("/", (req, res) => { path: "/thoughts", method: "POST", description: "Creates a new thought", - body:{ message: "Your happy thought (5-140 characters)" } + body: { message: "Your happy thought (5-140 characters)" } }, { - path: "/thoughts:/id/like", + path: "/thoughts/:id/like", method: "PATCH", description: "Likes a thought message" }, @@ -89,6 +79,7 @@ app.get("/", (req, res) => { }) }); +// route get all thoughts (with sorting option) app.get("/thoughts", async (req, res) => { try{ const { sort } = req.query @@ -107,6 +98,7 @@ app.get("/thoughts", async (req, res) => { } }); +// route to get a thought by ID app.get("/thoughts/:id", async (req,res) => { try { const { id } = req.params @@ -143,7 +135,7 @@ app.post("/thoughts", async (req, res) => { } }); -// route to delete thoughts +// route to delete thoughts by ID app.delete("/thoughts/:id", async (req, res) =>{ try { const { id } = req.params From cf0731371d4fed3a2c787d1a2f871c94a9a4b0af Mon Sep 17 00:00:00 2001 From: fridascript Date: Thu, 29 Jan 2026 15:23:50 +0100 Subject: [PATCH 5/7] adds updated error handling --- seed-database.js | 3 +- server.js | 320 ++++++++++++++++++++++++++++++----------------- 2 files changed, 207 insertions(+), 116 deletions(-) diff --git a/seed-database.js b/seed-database.js index a19230a..8d2c61d 100644 --- a/seed-database.js +++ b/seed-database.js @@ -1,5 +1,4 @@ -import dotenv from "dotenv" -dotenv.config() +import "dotenv/config"; import mongoose from "mongoose" import thoughtsData from "./data.json" with { type: "json" } diff --git a/server.js b/server.js index 8e64f6a..d3b6f87 100644 --- a/server.js +++ b/server.js @@ -1,24 +1,29 @@ -import dotenv from "dotenv" -dotenv.config() +import "dotenv/config"; import cors from "cors"; import express from "express"; import thoughtsData from "./data.json" with { type: "json" }; import mongoose from "mongoose"; +// connect to MongoDB +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/happythoughts"; +mongoose.connect(mongoUrl); +mongoose.Promise = Promise; -//connect to MongoDB -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/happythoughts" -mongoose.connect(mongoUrl) -mongoose.Promise = Promise +// defines the port the app will run on +const port = process.env.PORT || 8080; +const app = express(); -// define the thought model +// middlewares to enable cors and json body parsing +app.use(cors()); +app.use(express.json()); -const ThoughtSchema = new mongoose.Schema ({ +// define the thought model +const ThoughtSchema = new mongoose.Schema({ message: { type: String, required: true, - minglength: 5, - maxlength: 140 + minLength: 5, + maxLength: 140 }, hearts: { type: Number, @@ -28,150 +33,237 @@ const ThoughtSchema = new mongoose.Schema ({ type: Date, default: Date.now } -}) -const Thought = mongoose.model("Thought", ThoughtSchema) - +}); -// Defines the port the app will run on. -const port = process.env.PORT || 8080 -const app = express() +const Thought = mongoose.model("Thought", ThoughtSchema); -// Middlewares to enable cors and json body parsing -app.use(cors()) -app.use(express.json()) +// seed example from class (use RESET_DB=true npm run dev) +if (process.env.RESET_DB) { + const seedDatabase = async () => { + await Thought.deleteMany(); + + thoughtsData.forEach((thought) => { + new Thought(thought).save(); + }); + }; + seedDatabase(); +} -//routes +// routes -// documentation endpoints +// documentation endpoint app.get("/", (req, res) => { - - res.json ({ + res.json({ message: "Happy Thoughts API!", - endpoints: [ + endpoints: [ { - path: "/thoughts", - method: "GET", - description: "Returns all thoughts", - queryParams: "?sort=hearts or ?sort=date" - }, - { - path: "/thoughts/:id", - method: "GET", - description: "Returns a single thought by ID" - }, - { - path: "/thoughts", - method: "POST", - description: "Creates a new thought", - body: { message: "Your happy thought (5-140 characters)" } - }, - { - path: "/thoughts/:id/like", - method: "PATCH", - description: "Likes a thought message" - }, - { - path: "/thoughts/:id", - method: "DELETE", - description: "Deletes a thought by ID" - } + path: "/thoughts", + method: "GET", + description: "Returns all thoughts", + queryParams: "?sort=hearts or ?sort=date" + }, + { + path: "/thoughts/:id", + method: "GET", + description: "Returns a single thought by ID" + }, + { + path: "/thoughts", + method: "POST", + description: "Creates a new thought", + body: { message: "Your happy thought (5-140 characters)" } + }, + { + path: "/thoughts/:id/like", + method: "PATCH", + description: "Likes a thought (increments hearts by 1)" + }, + { + path: "/thoughts/:id", + method: "DELETE", + description: "Deletes a thought by ID" + } ] - }) -}); + }); +}); -// route get all thoughts (with sorting option) +// route to get all thoughts (with optional sorting) app.get("/thoughts", async (req, res) => { - try{ - const { sort } = req.query - let query = Thought.find() - - if (sort === "hearts") { - query = query.sort({ hearts: -1 }) - } else if (sort === "date") { - query.sort = query.sort ({ createdAt: -1}) - } + try { + const { sort } = req.query; + let query = Thought.find(); - const thoughts = await query.limit (25) - res.json(thoughts) -} catch (error) { - res.status(400).json({ error: "Sorry! Couldn't fetch any thoughts"}) -} + if (sort === "hearts") { + query = query.sort({ hearts: -1 }); + } else if (sort === "date") { + query = query.sort({ createdAt: -1 }); + } + + const thoughts = await query.limit(20); + + return res.status(200).json({ + success: true, + response: thoughts, + message: "Success" + }); + } catch (error) { + return res.status(500).json({ + success: false, + response: [], + message: "Could not fetch thoughts" + }); + } }); -// route to get a thought by ID -app.get("/thoughts/:id", async (req,res) => { +// route to get a single thought by ID +app.get("/thoughts/:id", async (req, res) => { try { - const { id } = req.params - const thought = await Thought.findById(id) + const { id } = req.params; + + // validate ID format before querying database + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).json({ + success: false, + response: null, + message: "Invalid ID format" + }); + } + + const thought = await Thought.findById(id); + + if (!thought) { + return res.status(404).json({ + success: false, + response: null, + message: "Thought not found" + }); + } - if (thought) { - res.json(thought) - }else { - res.status(404).json({error: "Sorry, no thought found"}) + return res.status(200).json({ + success: true, + response: thought, + message: "Success" + }); + } catch (error) { + return res.status(500).json({ + success: false, + response: null, + message: "Could not fetch thought" + }); } -} catch (error ) { - res.status(400).json({ error: "Invalid thought ID"}) -} }); -// route to create a new thought +// route to create a new thought app.post("/thoughts", async (req, res) => { - try{ - const { message } = req.body - - const newThought = await Thought.create ({ message }) - res.status(201).json(newThought) - + try { + const { message } = req.body; + const newThought = await Thought.create({ message }); + + return res.status(201).json({ + success: true, + response: newThought, + message: "Thought created successfully" + }); } catch (error) { - if (error.name === "ValidationError"){ - res.status(400).json({ - error: "Validation failed", + if (error.name === "ValidationError") { + return res.status(400).json({ + success: false, + response: null, + message: "Validation failed", details: error.message - }) - } else { - res.status(500).json({ error: "Could not create thought"}) + }); } + + return res.status(500).json({ + success: false, + response: null, + message: "Could not create thought" + }); } }); -// route to delete thoughts by ID -app.delete("/thoughts/:id", async (req, res) =>{ +// route to delete a thought by ID +app.delete("/thoughts/:id", async (req, res) => { try { - const { id } = req.params - const deletedThought = await Thought.findByIdAndDelete(id) + const { id } = req.params; + + // validate ID format before querying database + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).json({ + success: false, + response: null, + message: "Invalid ID format" + }); + } + + const deletedThought = await Thought.findByIdAndDelete(id); - if (deletedThought) { - res.json ({ message: "Thought deleted", deletedThought }) - } else { - res.status(404).json({ error: "Thought not found"}) + if (!deletedThought) { + return res.status(404).json({ + success: false, + response: null, + message: "Thought not found" + }); } + + return res.status(200).json({ + success: true, + response: deletedThought, + message: "Thought deleted successfully" + }); } catch (error) { - res.status(400).json({ error: "Could not delete thought" }) + return res.status(500).json({ + success: false, + response: null, + message: "Could not delete thought" + }); } }); -// route to like a thought -app.patch("/thoughts/:id/like", async (req,res) => { +// route to like a thought (increment hearts by 1) +app.patch("/thoughts/:id/like", async (req, res) => { try { - const { id } = req.params - const thought = await Thought.findByIdAndUpdate ( + const { id } = req.params; + + // validate ID format before querying database + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).json({ + success: false, + response: null, + message: "Invalid ID format" + }); + } + + const thought = await Thought.findByIdAndUpdate( id, - {$inc: {hearts: 1 }}, + { $inc: { hearts: 1 } }, { new: true } - ) + ); - if (thought) { - res.json(thought) - } else { - res.status(404).json({ error: "Thought not found"}) + if (!thought) { + return res.status(404).json({ + success: false, + response: null, + message: "Thought not found" + }); } + + return res.status(200).json({ + success: true, + response: thought, + message: "Thought liked successfully" + }); } catch (error) { - res.status(400).json({ error: "Could not update thought" }) + return res.status(500).json({ + success: false, + response: null, + message: "Could not like thought" + }); } }); -// Start the server +// start the server app.listen(port, () => { - console.log(`Server running on http://localhost:${port}`) -}); + console.log(`Server running on http://localhost:${port}`); +}); \ No newline at end of file From 7ccaac8ee5ed7c9d664c0c4fdb1d21edbc4d255b Mon Sep 17 00:00:00 2001 From: fridascript Date: Wed, 4 Feb 2026 18:00:32 +0100 Subject: [PATCH 6/7] Adds user atuhentication with bcrypt --- package.json | 1 + server.js | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 128 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6c624c0..baf9d2f 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@babel/core": "^7.17.9", "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", + "bcrypt": "^6.0.0", "cors": "^2.8.5", "dotenv": "^17.2.3", "express": "^4.17.3", diff --git a/server.js b/server.js index d3b6f87..2c8bd38 100644 --- a/server.js +++ b/server.js @@ -3,12 +3,60 @@ import cors from "cors"; import express from "express"; import thoughtsData from "./data.json" with { type: "json" }; import mongoose from "mongoose"; +import crypto from "crypto"; +import bcrypt from "bcrypt"; // connect to MongoDB const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/happythoughts"; mongoose.connect(mongoUrl); mongoose.Promise = Promise; +const UserSchema = new mongoose.Schema({ + name:{ + type: String, + unique: true + }, + email: { + type: String, + unique: true, + required: true + }, + password: { + type: String, + required: true + }, + accessToken: { + type: String, + default: () => crypto.randomBytes(128).toString("hex") + } +}); + +const User = mongoose.model("User", UserSchema); + +//authentication middleware (checks if user has valid token) +const authenticateUser = async (req, res, next) => { + const accessToken = req.header("Authorization"); + try { + const user = await User.findOne({ accessToken }); + if (user) { + req.user = user; + next(); + } else { + return res.status(401).json({ + success: false, + response: null, + message: "please log in" + }); + } + } catch (error) { + return res.status(500).json({ + success: false, + response: null, + message: "Authentication failed" + }); + } +}; + // defines the port the app will run on const port = process.env.PORT || 8080; const app = express(); @@ -56,6 +104,18 @@ app.get("/", (req, res) => { res.json({ message: "Happy Thoughts API!", endpoints: [ + { + path: "/users", + method: "POST", + description: "Register a new user", + body: { name: "string", email:"string", password:"string"} + }, + { + path: "/sessions", + method: "POST", + description: "Log in (returns accessToken)", + body: { email: "string", password:"string"} + }, { path: "/thoughts", method: "GET", @@ -115,6 +175,71 @@ app.get("/thoughts", async (req, res) => { } }); +// route to register a new user + +app.post("/users", async (req, res) => { + try { + const {name, email, password } = req.body; + + const salt = bcrypt.genSaltSync(); + + const user = new User({ + name, + email, + password: bcrypt.hashSync(password, salt) + }); + + await user.save(); + + res.status(201).json({ + id: user._id, + accessToken: user.accessToken + }); + } catch (err) { + res.status(400).json({ + message: "could not create user", + errors: err.errors + }); + } +}); + +// route to log in (sessions) + +app.post("/sessions", async (req,res) => { + try { + const { email, password } = req.body; + + //find user by email + const user = await User.findOne ({ email }); + + // check if user exists and password is correct + if (user && bcrypt.compareSync(password, user.password)) { + return res.status(200).json({ + success: true, + response: { + userId: user._id, + name: user.name, + email: user.email, + accessToken: user.accessToken + }, + message: "Login successful" + }); + } else { + return res.status(401).json({ + success: false, + response: null, + message: "Invalid email or password" + }); + } + } catch (error) { + return res.status(500).json({ + success: false, + response: null, + message: "Could not log in" + }); + } +}); + // route to get a single thought by ID app.get("/thoughts/:id", async (req, res) => { try { @@ -154,7 +279,7 @@ app.get("/thoughts/:id", async (req, res) => { }); // route to create a new thought -app.post("/thoughts", async (req, res) => { +app.post("/thoughts", authenticateUser, async (req, res) => { try { const { message } = req.body; @@ -184,7 +309,7 @@ app.post("/thoughts", async (req, res) => { }); // route to delete a thought by ID -app.delete("/thoughts/:id", async (req, res) => { +app.delete("/thoughts/:id", authenticateUser, async (req, res) => { try { const { id } = req.params; From 419b9a6ef5943132821e5e760d21bb4690f3781d Mon Sep 17 00:00:00 2001 From: fridascript Date: Wed, 4 Feb 2026 18:15:02 +0100 Subject: [PATCH 7/7] adds updated endpoints with authentication --- server.js | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index 2c8bd38..7f8070a 100644 --- a/server.js +++ b/server.js @@ -130,9 +130,17 @@ app.get("/", (req, res) => { { path: "/thoughts", method: "POST", - description: "Creates a new thought", + description: "Creates a new thought (req authentication)", + headers: { Authorization: "accessToken"}, body: { message: "Your happy thought (5-140 characters)" } }, + { + path: "/thoughts/:id", + method: "PATCH", + description: "Updates a thought message (req authentication)", + headers: { Authorization: "accessToken" }, + body: { message: "Updated thought (5-140 characters)" } + }, { path: "/thoughts/:id/like", method: "PATCH", @@ -141,7 +149,8 @@ app.get("/", (req, res) => { { path: "/thoughts/:id", method: "DELETE", - description: "Deletes a thought by ID" + description: "Deletes a thought by ID (req authentication)", + headers: { Authorization: "accessToken" } } ] }); @@ -346,6 +355,58 @@ app.delete("/thoughts/:id", authenticateUser, async (req, res) => { } }); +// route to update a thought message (authenticated) +app.patch("/thoughts/:id", authenticateUser, async (req, res) => { + try { + const { id } = req.params; + const { message } = req.body; + + // validate ID format before querying database + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).json({ + success: false, + response: null, + message: "Invalid ID format" + }); + } + + // validate message length + if (!message || message.length < 5 || message.length > 140) { + return res.status(400).json({ + success: false, + response: null, + message: "Message must be between 5 and 140 characters" + }); + } + + const updatedThought = await Thought.findByIdAndUpdate( + id, + { message }, + { new: true, runValidators: true } + ); + + if (!updatedThought) { + return res.status(404).json({ + success: false, + response: null, + message: "Thought not found" + }); + } + + return res.status(200).json({ + success: true, + response: updatedThought, + message: "Thought updated successfully" + }); + } catch (error) { + return res.status(500).json({ + success: false, + response: null, + message: "Could not update thought" + }); + } +}); + // route to like a thought (increment hearts by 1) app.patch("/thoughts/:id/like", async (req, res) => { try {