From 1c968cdb5dd6b107306c322d8fa9b11cfb15ccad Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Tue, 20 Jan 2026 15:08:43 +0100 Subject: [PATCH 01/19] adding endpoints to server.js --- package.json | 1 + server.js | 25 ++++++++++++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index bf25bb6..00addae 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", + "express-list-endpoints": "^7.1.1", "nodemon": "^3.0.1" } } diff --git a/server.js b/server.js index f47771b..9677e93 100644 --- a/server.js +++ b/server.js @@ -1,21 +1,40 @@ import cors from "cors" import express from "express" +import data from "./data.json" with { type: "json" }; +import listEndpoints from "express-list-endpoints"; + // 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 -const port = process.env.PORT || 8080 +const port = process.env.PORT || 9090 const app = express() // Add middlewares to enable cors and json body parsing app.use(cors()) app.use(express.json()) -// Start defining your routes here +// Start defining your routes here. Endpoint! Response-object app.get("/", (req, res) => { - res.send("Hello Technigo!") + const endpoints = listEndpoints(app); + + res.json([{ + message: "Welcome to the thoughts API", + endpoints: endpoints, + }]) +}); + +app.get("/thoughts", (req, res) => { + res.json(data) }) +app.get("/thoughts/:id", (req, res) => { + const id = req.params.id + const thoughtsId = data.find((thought) => (thought._id) === (id)); + res.json(thoughtsId) +}) + + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`) From ec5928bbb7b86c4a2a9dd9e4608aa69490eb64f4 Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Thu, 22 Jan 2026 14:08:13 +0100 Subject: [PATCH 02/19] adding error handling to thoughtsId --- server.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/server.js b/server.js index 9677e93..d9d30fe 100644 --- a/server.js +++ b/server.js @@ -19,21 +19,33 @@ app.get("/", (req, res) => { const endpoints = listEndpoints(app); res.json([{ - message: "Welcome to the thoughts API", + message: "Welcome to the Happy thoughts API", endpoints: endpoints, }]) }); +//Endpoint for all the thoughts app.get("/thoughts", (req, res) => { res.json(data) }) +//Endpoint for the thoughts id app.get("/thoughts/:id", (req, res) => { const id = req.params.id const thoughtsId = data.find((thought) => (thought._id) === (id)); + + if (!thoughtsId) { + return res + .status(404) + .json({ error: `Thought with id ${id} does not exist` }) + } + res.json(thoughtsId) }) +//Endpoint for hearts/likes + + // Start the server app.listen(port, () => { From cfa286778dd1e8896c8194282ce2d641c4d05e79 Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Thu, 22 Jan 2026 14:48:35 +0100 Subject: [PATCH 03/19] adding new endpoint, thoughts/hearts/:amount --- server.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/server.js b/server.js index d9d30fe..0bb97df 100644 --- a/server.js +++ b/server.js @@ -43,8 +43,29 @@ app.get("/thoughts/:id", (req, res) => { res.json(thoughtsId) }) -//Endpoint for hearts/likes +//Endpoint for hearts amount +app.get("/thoughts/hearts/:amount", (req, res) => { + const amount = req.params.amount //Hämta amount från parametern + const minHearts = Number(amount) //Konvertera till number + //isNaN = is Not a Number. Om användare skulle ange något annat än ett nr, errormeddelandet upp. + if (isNaN(minHearts)) { + return res + .status(400) + .json({ error: `The amount must be a number` }) + } + + const filteredThoughts = data.filter((thought) => thought.hearts >= minHearts) //filtrera datan - spara endast thoughts där hearts >= minHearts + + if (filteredThoughts.length === 0) { + return res + .status(404) + .json({ error: `No thoughts found with ${amount} or more hearts` }) + } + + res.json(filteredThoughts); //returnera resultat + +}) // Start the server From 7fb4dd5d70e05903123b109c1cb6190d4232bd55 Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Mon, 26 Jan 2026 14:07:13 +0100 Subject: [PATCH 04/19] adding mongoose, and changing the endpoints --- package.json | 1 + server.js | 95 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 63 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 00addae..214a34f 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "cors": "^2.8.5", "express": "^4.17.3", "express-list-endpoints": "^7.1.1", + "mongoose": "^9.1.5", "nodemon": "^3.0.1" } } diff --git a/server.js b/server.js index 0bb97df..a027cf0 100644 --- a/server.js +++ b/server.js @@ -1,7 +1,9 @@ -import cors from "cors" +import cors from "cors" //tillåter req från andra webplatser import express from "express" import data from "./data.json" with { type: "json" }; import listEndpoints from "express-list-endpoints"; +import mongoose from "mongoose"; +import { Schema, model } from "mongoose"; // Defines the port the app will run on. Defaults to 8080, but can be overridden @@ -14,7 +16,25 @@ const app = express() app.use(cors()) app.use(express.json()) -// Start defining your routes here. Endpoint! Response-object +//Using MongoDB, connecting with database. +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/thoughts" +mongoose.connect(mongoUrl) +mongoose.Promise = Promise + +//varje tanke som sparas i databasen måste följa denna struktur. Skapar mallen för datan. +const thoughtSchema = new Schema({ + message: String, + hearts: Number, + createdAt: Date +}) + +//skapar verktyget för att hantera datan. Thought = namnet på samlingen i databasen. thoughtSchema = mallen vi skapade ovan. +const Thought = model("thought", thoughtSchema) + + +// ENDPOINTS // + +// Showing all the endpoints app.get("/", (req, res) => { const endpoints = listEndpoints(app); @@ -25,50 +45,59 @@ app.get("/", (req, res) => { }); //Endpoint for all the thoughts -app.get("/thoughts", (req, res) => { - res.json(data) +app.get("/thoughts", async (req, res) => { + try { + const thoughts = await Thought.find() + res.json(thoughts) + } catch (error) { + res.status(500).json({ error: "Could not fetch thoughts" }) + } }) -//Endpoint for the thoughts id -app.get("/thoughts/:id", (req, res) => { - const id = req.params.id - const thoughtsId = data.find((thought) => (thought._id) === (id)); - if (!thoughtsId) { - return res - .status(404) - .json({ error: `Thought with id ${id} does not exist` }) - } - res.json(thoughtsId) -}) +//Endpoint for the thoughts id, to get one thought +app.get("/thoughts/:id", async (req, res) => { + try { + const id = req.params.id + const thoughtsId = await Thought.findById(id) -//Endpoint for hearts amount -app.get("/thoughts/hearts/:amount", (req, res) => { - const amount = req.params.amount //Hämta amount från parametern - const minHearts = Number(amount) //Konvertera till number + if (!thoughtsId) { + return res.status(404).json({ error: `Thought with id ${id} does not exist` }) + } + res.json(thoughtsId) - //isNaN = is Not a Number. Om användare skulle ange något annat än ett nr, errormeddelandet upp. - if (isNaN(minHearts)) { - return res - .status(400) - .json({ error: `The amount must be a number` }) + } catch (error) { + return res.status(500).json({ error: `Could not fetch thoughts` }) } +}) - const filteredThoughts = data.filter((thought) => thought.hearts >= minHearts) //filtrera datan - spara endast thoughts där hearts >= minHearts +//Endpoint for hearts amount, gets thoughts with x amount of hearts +app.get("/thoughts/hearts/:amount", async (req, res) => { + try { + const amount = req.params.amount + const minHearts = Number(amount) - if (filteredThoughts.length === 0) { - return res - .status(404) - .json({ error: `No thoughts found with ${amount} or more hearts` }) - } + //isNaN = is Not a Number. Om användare skulle ange något annat än ett nr, errormeddelandet upp. + if (isNaN(minHearts)) { + return res + .status(400) + .json({ error: `The amount must be a number` }) + } - res.json(filteredThoughts); //returnera resultat + const filteredThoughts = await Thought.find({ hearts: { $gte: minHearts } }) -}) + if (filteredThoughts.length === 0) { + return res.status(404).json({ error: `No thoughts found with ${amount} or more hearts` }) + } + res.json(filteredThoughts); //returnera resultat + } catch (error) { + res.status(500).json({ error: `Could not fetch thoughts` }) + } +}) // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`) -}) +}) \ No newline at end of file From ca093489b8446d35eae717881cec5a2f9f36119c Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Mon, 26 Jan 2026 14:46:05 +0100 Subject: [PATCH 05/19] adding middleware. errorhandling if database is down. --- server.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/server.js b/server.js index a027cf0..6a2a335 100644 --- a/server.js +++ b/server.js @@ -16,6 +16,15 @@ const app = express() app.use(cors()) app.use(express.json()) +//Error if the database is not responding. Also a middleware. +app.use((req, res, next) => { + if (mongoose.connection.readyState === 1) { + next() + } else { + res.status(503).json({ error: `Server unavailable` }) + } +}) + //Using MongoDB, connecting with database. const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/thoughts" mongoose.connect(mongoUrl) @@ -54,13 +63,10 @@ app.get("/thoughts", async (req, res) => { } }) - - //Endpoint for the thoughts id, to get one thought app.get("/thoughts/:id", async (req, res) => { try { - const id = req.params.id - const thoughtsId = await Thought.findById(id) + const thoughtsId = await Thought.findById(req.params.id) if (!thoughtsId) { return res.status(404).json({ error: `Thought with id ${id} does not exist` }) @@ -75,8 +81,7 @@ app.get("/thoughts/:id", async (req, res) => { //Endpoint for hearts amount, gets thoughts with x amount of hearts app.get("/thoughts/hearts/:amount", async (req, res) => { try { - const amount = req.params.amount - const minHearts = Number(amount) + const minHearts = Number(req.params.amount) //isNaN = is Not a Number. Om användare skulle ange något annat än ett nr, errormeddelandet upp. if (isNaN(minHearts)) { From bc751f165f274531c4b3684c23468e917fc7512d Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Wed, 28 Jan 2026 14:30:41 +0100 Subject: [PATCH 06/19] adding post thoughts --- server.js | 46 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/server.js b/server.js index 6a2a335..84406f9 100644 --- a/server.js +++ b/server.js @@ -5,6 +5,7 @@ import listEndpoints from "express-list-endpoints"; import mongoose from "mongoose"; import { Schema, model } from "mongoose"; +//MONGO_URL=mongodb://localhost:27017/happy-thoughts // 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: @@ -16,7 +17,7 @@ const app = express() app.use(cors()) app.use(express.json()) -//Error if the database is not responding. Also a middleware. +// Error if the database is not responding. Also a middleware. app.use((req, res, next) => { if (mongoose.connection.readyState === 1) { next() @@ -25,24 +26,32 @@ app.use((req, res, next) => { } }) -//Using MongoDB, connecting with database. +// Using MongoDB, connecting with database. const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/thoughts" -mongoose.connect(mongoUrl) +mongoose.connect(mongoUrl); mongoose.Promise = Promise -//varje tanke som sparas i databasen måste följa denna struktur. Skapar mallen för datan. +// varje tanke som sparas i databasen måste följa denna struktur. Skapar mallen för datan. const thoughtSchema = new Schema({ - message: String, - hearts: Number, - createdAt: Date + message: { + type: String, + required: true + }, + hearts: { + type: Number, + default: 0, + }, + createdAt: { + type: Date, + default: () => new Date() + } }) -//skapar verktyget för att hantera datan. Thought = namnet på samlingen i databasen. thoughtSchema = mallen vi skapade ovan. -const Thought = model("thought", thoughtSchema) +// skapar verktyget för att hantera datan. Thought = namnet på samlingen i databasen. thoughtSchema = mallen vi skapade ovan. +const Thought = mongoose.model("thought", thoughtSchema) // ENDPOINTS // - // Showing all the endpoints app.get("/", (req, res) => { const endpoints = listEndpoints(app); @@ -102,6 +111,23 @@ app.get("/thoughts/hearts/:amount", async (req, res) => { } }) +//Här sparas nya thoughts som användaren skapar. Här skapas nya thoughts. +app.post("/thoughts", async (req, res) => { + try { + const { message } = req.body + + if (!message || message.trim().length === 0) { + return res.status(400).json({ error: `Message is required` }) + } + + const newThought = await Thought.create({ message }) + + res.status(201).json(newThought) + } catch (error) { + res.status(500).json({ error: `Could not create thought` }) + } +}) + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`) From 884ebd41c3b798c20916de7a93fa1187590fb27f Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Wed, 28 Jan 2026 15:45:54 +0100 Subject: [PATCH 07/19] adding .env file --- package.json | 1 + server.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/package.json b/package.json index 214a34f..e3f8894 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", "express-list-endpoints": "^7.1.1", "mongoose": "^9.1.5", diff --git a/server.js b/server.js index 84406f9..06f28a8 100644 --- a/server.js +++ b/server.js @@ -4,6 +4,8 @@ import data from "./data.json" with { type: "json" }; import listEndpoints from "express-list-endpoints"; import mongoose from "mongoose"; import { Schema, model } from "mongoose"; +import "dotenv/config"; + //MONGO_URL=mongodb://localhost:27017/happy-thoughts From b53ae1b82495b8db3278426045fcabd96687417c Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Thu, 29 Jan 2026 09:28:13 +0100 Subject: [PATCH 08/19] adding seeding so it only renders once when emty --- server.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/server.js b/server.js index 06f28a8..23bfa3e 100644 --- a/server.js +++ b/server.js @@ -33,8 +33,8 @@ const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/thoughts" mongoose.connect(mongoUrl); mongoose.Promise = Promise -// varje tanke som sparas i databasen måste följa denna struktur. Skapar mallen för datan. -const thoughtSchema = new Schema({ +// varje tanke som sparas i databasen måste följa denna struktur/schema. Skapar mallen för datan. +const thoughtSchema = new mongoose.Schema({ message: { type: String, required: true @@ -49,9 +49,25 @@ const thoughtSchema = new Schema({ } }) -// skapar verktyget för att hantera datan. Thought = namnet på samlingen i databasen. thoughtSchema = mallen vi skapade ovan. +// skapar verktyget/model för att hantera datan. Thought = namnet på samlingen i databasen. thoughtSchema = mallen vi skapade ovan. const Thought = mongoose.model("thought", thoughtSchema) +//Seeding of DB, resetting the database +if (process.env.RESET_DB) { + const seedDatabase = async () => { + const count = await Thought.countDocuments() //Räknar dokument + if (count > 0) { + return //om det finns dokument redan, gör inget + } + //om vi kommer hit, databasen är tom - seeda! + await Thought.deleteMany() + data.forEach(thought => { + new Thought(thought).save() + }) + } + + seedDatabase() +} // ENDPOINTS // // Showing all the endpoints From f4d4444bf71a2325274cbeb1b588b717cf58bdb2 Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Thu, 29 Jan 2026 15:20:02 +0100 Subject: [PATCH 09/19] adding comments --- server.js | 58 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/server.js b/server.js index 23bfa3e..c7dd81c 100644 --- a/server.js +++ b/server.js @@ -33,7 +33,7 @@ const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/thoughts" mongoose.connect(mongoUrl); mongoose.Promise = Promise -// varje tanke som sparas i databasen måste följa denna struktur/schema. Skapar mallen för datan. +// Schema const thoughtSchema = new mongoose.Schema({ message: { type: String, @@ -49,7 +49,7 @@ const thoughtSchema = new mongoose.Schema({ } }) -// skapar verktyget/model för att hantera datan. Thought = namnet på samlingen i databasen. thoughtSchema = mallen vi skapade ovan. +// Model const Thought = mongoose.model("thought", thoughtSchema) //Seeding of DB, resetting the database @@ -69,8 +69,7 @@ if (process.env.RESET_DB) { seedDatabase() } -// ENDPOINTS // -// Showing all the endpoints +//GET-method. Showing all the endpoints and documentation. app.get("/", (req, res) => { const endpoints = listEndpoints(app); @@ -80,7 +79,7 @@ app.get("/", (req, res) => { }]) }); -//Endpoint for all the thoughts +//GET-method. Endpoint for all the thoughts. app.get("/thoughts", async (req, res) => { try { const thoughts = await Thought.find() @@ -90,7 +89,7 @@ app.get("/thoughts", async (req, res) => { } }) -//Endpoint for the thoughts id, to get one thought +//GET-method. Endpoint for the thoughts id, to get one specific thought. app.get("/thoughts/:id", async (req, res) => { try { const thoughtsId = await Thought.findById(req.params.id) @@ -105,12 +104,12 @@ app.get("/thoughts/:id", async (req, res) => { } }) -//Endpoint for hearts amount, gets thoughts with x amount of hearts +//GET-method. Endpoint for hearts amount, gets thoughts with x amount of hearts app.get("/thoughts/hearts/:amount", async (req, res) => { try { const minHearts = Number(req.params.amount) - //isNaN = is Not a Number. Om användare skulle ange något annat än ett nr, errormeddelandet upp. + //isNaN = is Not a Number if (isNaN(minHearts)) { return res .status(400) @@ -122,14 +121,14 @@ app.get("/thoughts/hearts/:amount", async (req, res) => { if (filteredThoughts.length === 0) { return res.status(404).json({ error: `No thoughts found with ${amount} or more hearts` }) } - res.json(filteredThoughts); //returnera resultat + res.json(filteredThoughts); } catch (error) { res.status(500).json({ error: `Could not fetch thoughts` }) } }) -//Här sparas nya thoughts som användaren skapar. Här skapas nya thoughts. +//POST-method. Adding a new message to the database app.post("/thoughts", async (req, res) => { try { const { message } = req.body @@ -146,6 +145,45 @@ app.post("/thoughts", async (req, res) => { } }) +//PATCH-Method, updates a thought when liked. +app.patch("/thoughts/:id", async (req, res) => { + try { + const id = req.params.id + const { hearts } = req.body + if (isNaN(hearts)) { + return res.status(400).json({ error: "Hearts must be a number" }) + } + const updatedThought = await Thought.findByIdAndUpdate( + id, + { hearts: hearts }, + { new: true } + ) + + if (!updatedThought) { + return res.status(404).json({ error: "Thought not found" }) + } + res.json(updatedThought) + + } catch (error) { + res.json(500).json({ error: "Could not update thought" }) + } +}) + +//DELETE-method, deletes a thought +app.delete("/thoughts/:id", async (req, res) => { + try { + const id = req.params.id + const deletedThought = await Thought.findByIdAndDelete(id) + + if (!deletedThought) { + return res.status(404).json({ error: `Thought with id ${id} does not exist` }) + } + res.json(deletedThought) + } catch (error) { + res.status(500).json({ error: "Could not delete thought " }) + } +}) + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`) From 0bdae33d277aa1b66b7fa9bd891fc92b20dd3c79 Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Mon, 2 Feb 2026 14:40:29 +0100 Subject: [PATCH 10/19] adding authentication logic --- package.json | 2 + server.js | 159 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 129 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index e3f8894..0f599f4 100644 --- a/package.json +++ b/package.json @@ -12,11 +12,13 @@ "@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", "express-list-endpoints": "^7.1.1", "mongoose": "^9.1.5", + "node.js": "^0.0.1-security", "nodemon": "^3.0.1" } } diff --git a/server.js b/server.js index c7dd81c..6d0d8e4 100644 --- a/server.js +++ b/server.js @@ -1,13 +1,12 @@ -import cors from "cors" //tillåter req från andra webplatser -import express from "express" +import cors from "cors"; +import express from "express"; import data from "./data.json" with { type: "json" }; import listEndpoints from "express-list-endpoints"; import mongoose from "mongoose"; import { Schema, model } from "mongoose"; import "dotenv/config"; - - -//MONGO_URL=mongodb://localhost:27017/happy-thoughts +import crypto from "crypto"; +import bcrypt from "bcrypt" // 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: @@ -29,11 +28,30 @@ app.use((req, res, next) => { }) // Using MongoDB, connecting with database. -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/thoughts" +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/auth" mongoose.connect(mongoUrl); mongoose.Promise = Promise // Schema +const userSchema = new mongoose.Schema({ + name: { + type: String, + unique: true + }, + email: { + type: String, + unique: true, + }, + password: { + type: String, + required: true + }, + accessToken: { + type: String, + default: () => crypto.randomBytes(128).toString("hex") + } +}); + const thoughtSchema = new mongoose.Schema({ message: { type: String, @@ -49,25 +67,24 @@ const thoughtSchema = new mongoose.Schema({ } }) -// Model -const Thought = mongoose.model("thought", thoughtSchema) +// Models +const User = mongoose.model("User", userSchema); +const Thought = mongoose.model("thought", thoughtSchema); -//Seeding of DB, resetting the database -if (process.env.RESET_DB) { - const seedDatabase = async () => { - const count = await Thought.countDocuments() //Räknar dokument - if (count > 0) { - return //om det finns dokument redan, gör inget - } - //om vi kommer hit, databasen är tom - seeda! - await Thought.deleteMany() - data.forEach(thought => { - new Thought(thought).save() - }) +// Middelware-function that looks up the user +const authenticateUser = async (req, res, next) => { + const user = await User.findOne({ + accessToken: req.header("Authorization") + }); + if (user) { + req.user = user + next(); + } else { + res.status(401).json({ + loggedOut: true + }); } - - seedDatabase() -} +}; //GET-method. Showing all the endpoints and documentation. app.get("/", (req, res) => { @@ -95,7 +112,7 @@ app.get("/thoughts/:id", async (req, res) => { const thoughtsId = await Thought.findById(req.params.id) if (!thoughtsId) { - return res.status(404).json({ error: `Thought with id ${id} does not exist` }) + return res.status(404).json({ error: `Thought with id ${thoughtsId} does not exist` }) } res.json(thoughtsId) @@ -119,7 +136,7 @@ app.get("/thoughts/hearts/:amount", async (req, res) => { const filteredThoughts = await Thought.find({ hearts: { $gte: minHearts } }) if (filteredThoughts.length === 0) { - return res.status(404).json({ error: `No thoughts found with ${amount} or more hearts` }) + return res.status(404).json({ error: `No thoughts found with ${minHearts} or more hearts` }) } res.json(filteredThoughts); @@ -145,27 +162,80 @@ app.post("/thoughts", async (req, res) => { } }) +//POST-Method, creates a new user. Registration endpoint. +app.post("/users", async (req, res) => { + try { + const { name, email, password } = req.body; + + if (!name || !email || !password) { + return res.status(400).json({ error: "Name, email and password are required" }); + } + + const salt = bcrypt.genSaltSync() + const user = new User({ name, email, password: bcrypt.hashSync(password, salt) }); + await user.save(); + res.status(201).json({ + success: true, + message: "User created", + id: user._id, + accessToken: user.accessToken + }); + } catch (error) { + console.log(error); + res.status(400).json({ + success: false, + message: "Could not create user", + errors: error + }); + } +}); + +app.post("/sessions", async (req, res) => { + const user = await User.findOne({ + email: req.body.email + }) + if (user && bcrypt.compareSync(req.body.password, user.password)) { + res.json({ userId: user._id }) + } else { + res.json({ notFound: true }) + } +}) + //PATCH-Method, updates a thought when liked. app.patch("/thoughts/:id", async (req, res) => { try { const id = req.params.id - const { hearts } = req.body - if (isNaN(hearts)) { - return res.status(400).json({ error: "Hearts must be a number" }) + const { message, hearts } = req.body + + const update = {} + + if (message !== undefined) { + if (message.trim().length === 0) { + return res.status(400).json({ error: "Message can not be empty" }) + } + update.message = message + } + + if (hearts !== undefined) { + if (isNaN(hearts)) { + return res.status(400).json({ error: "Hearts must be a number" }) + } + update.hearts = hearts } + const updatedThought = await Thought.findByIdAndUpdate( id, - { hearts: hearts }, + update, { new: true } ) if (!updatedThought) { return res.status(404).json({ error: "Thought not found" }) } - res.json(updatedThought) + res.json(updatedThought) } catch (error) { - res.json(500).json({ error: "Could not update thought" }) + res.status(500).json({ error: "Could not update thought" }) } }) @@ -184,7 +254,32 @@ app.delete("/thoughts/:id", async (req, res) => { } }) +// GET-method, secret endpoint. +app.get("/secrets", authenticateUser); +app.get("/secrets", (req, res) => { + res.json({ secret: "This is a super secret message." }) +}) + +// Log-in endpoint. Finds user. +app.post("/sessions", async () => { + const user = await User.findOne({ email: req.body.email }); + if (user && bcrypt.compareSync(req.body.password, user.password)) { + res.json({ userId: user._id, accessToken: user.accessToken }); + } else { + res.json({ notFound: true }); + } +}); + // Start the server -app.listen(port, () => { +const server = app.listen(port, () => { console.log(`Server running on http://localhost:${port}`) +}) + +// Graceful shutdown for Nodemon +process.on('SIGTERM', () => { + console.log('Server shutting down...') + server.close(() => { + mongoose.connection.close() + process.exit(0) + }) }) \ No newline at end of file From 81a9c5e7160da075d262fdbdc8771800d064ea26 Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Tue, 3 Feb 2026 09:08:24 +0100 Subject: [PATCH 11/19] adding comments + debugging to make delete and edit work --- server.js | 170 ++++++++++++++++++++++++------------------------------ 1 file changed, 75 insertions(+), 95 deletions(-) diff --git a/server.js b/server.js index 6d0d8e4..1ba3adc 100644 --- a/server.js +++ b/server.js @@ -1,16 +1,12 @@ import cors from "cors"; import express from "express"; -import data from "./data.json" with { type: "json" }; import listEndpoints from "express-list-endpoints"; import mongoose from "mongoose"; -import { Schema, model } from "mongoose"; import "dotenv/config"; import crypto from "crypto"; -import bcrypt from "bcrypt" +import bcrypt from "bcrypt"; -// 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 +// ======= Config & setup ======= const port = process.env.PORT || 9090 const app = express() @@ -25,14 +21,14 @@ app.use((req, res, next) => { } else { res.status(503).json({ error: `Server unavailable` }) } -}) +}); -// Using MongoDB, connecting with database. +// ======= Database connection ======= const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/auth" mongoose.connect(mongoUrl); mongoose.Promise = Promise -// Schema +// ======= Schemas & Models ======= const userSchema = new mongoose.Schema({ name: { type: String, @@ -67,11 +63,10 @@ const thoughtSchema = new mongoose.Schema({ } }) -// Models const User = mongoose.model("User", userSchema); const Thought = mongoose.model("thought", thoughtSchema); -// Middelware-function that looks up the user +// ======= Middleware-function ======= const authenticateUser = async (req, res, next) => { const user = await User.findOne({ accessToken: req.header("Authorization") @@ -86,7 +81,52 @@ const authenticateUser = async (req, res, next) => { } }; -//GET-method. Showing all the endpoints and documentation. +// ======= Authentication routes (Login/Signup) ======= +// Creates a new user. Registration endpoint. +app.post("/users", async (req, res) => { + try { + const { name, email, password } = req.body; + + if (!name || !email || !password) { + return res.status(400).json({ error: "Name, email and password are required" }); + } + + const salt = bcrypt.genSaltSync() + const user = new User({ name, email, password: bcrypt.hashSync(password, salt) }); + await user.save(); + res.status(201).json({ + success: true, + message: "User created", + id: user._id, + accessToken: user.accessToken + }); + } catch (error) { + console.log(error); + res.status(400).json({ + success: false, + message: "Could not create user", + errors: error + }); + } +}); + +// POST-method. Log-in endpoint. Finds user. +app.post("/sessions", async (req, res) => { + const user = await User.findOne({ email: req.body.email }); + if (user && bcrypt.compareSync(req.body.password, user.password)) { + res.json({ userId: user._id, accessToken: user.accessToken }); + } else { + res.json({ notFound: true }); + } +}); + +// ======= Protected Routes ======= +app.get("/secrets", authenticateUser, (req, res) => { + res.json({ secret: "This is a super secret message." }) +}); + +// ======= Thoughts Routes ======= +// Showing all the endpoints and documentation. app.get("/", (req, res) => { const endpoints = listEndpoints(app); @@ -96,56 +136,32 @@ app.get("/", (req, res) => { }]) }); -//GET-method. Endpoint for all the thoughts. +// Endpoint for all the thoughts. app.get("/thoughts", async (req, res) => { try { const thoughts = await Thought.find() - res.json(thoughts) + res.json(thoughts); } catch (error) { res.status(500).json({ error: "Could not fetch thoughts" }) } -}) +}); -//GET-method. Endpoint for the thoughts id, to get one specific thought. +// Endpoint for the thoughts id, to get one specific thought. app.get("/thoughts/:id", async (req, res) => { try { const thoughtsId = await Thought.findById(req.params.id) if (!thoughtsId) { - return res.status(404).json({ error: `Thought with id ${thoughtsId} does not exist` }) + return res.status(404).json({ error: `Thought with id ${req.params.id} does not exist` }) } res.json(thoughtsId) } catch (error) { return res.status(500).json({ error: `Could not fetch thoughts` }) } -}) - -//GET-method. Endpoint for hearts amount, gets thoughts with x amount of hearts -app.get("/thoughts/hearts/:amount", async (req, res) => { - try { - const minHearts = Number(req.params.amount) - - //isNaN = is Not a Number - if (isNaN(minHearts)) { - return res - .status(400) - .json({ error: `The amount must be a number` }) - } - - const filteredThoughts = await Thought.find({ hearts: { $gte: minHearts } }) - - if (filteredThoughts.length === 0) { - return res.status(404).json({ error: `No thoughts found with ${minHearts} or more hearts` }) - } - res.json(filteredThoughts); - - } catch (error) { - res.status(500).json({ error: `Could not fetch thoughts` }) - } -}) +}); -//POST-method. Adding a new message to the database +// Adding a new message to the database app.post("/thoughts", async (req, res) => { try { const { message } = req.body @@ -160,48 +176,28 @@ app.post("/thoughts", async (req, res) => { } catch (error) { res.status(500).json({ error: `Could not create thought` }) } -}) +}); -//POST-Method, creates a new user. Registration endpoint. -app.post("/users", async (req, res) => { +// Endpoint for liking a thought +app.post("/thoughts/:id/like", async (req, res) => { try { - const { name, email, password } = req.body; + const id = req.params.id; + const thought = await Thought.findById(id); - if (!name || !email || !password) { - return res.status(400).json({ error: "Name, email and password are required" }); + if (!thought) { + return res.status(404).json({ error: "Thought not found" }); } + thought.hearts += 1; + await thought.save(); + res.json(thought); - const salt = bcrypt.genSaltSync() - const user = new User({ name, email, password: bcrypt.hashSync(password, salt) }); - await user.save(); - res.status(201).json({ - success: true, - message: "User created", - id: user._id, - accessToken: user.accessToken - }); } catch (error) { - console.log(error); - res.status(400).json({ - success: false, - message: "Could not create user", - errors: error - }); + res.status(500).json({ error: "Could not like thought" }); } }); -app.post("/sessions", async (req, res) => { - const user = await User.findOne({ - email: req.body.email - }) - if (user && bcrypt.compareSync(req.body.password, user.password)) { - res.json({ userId: user._id }) - } else { - res.json({ notFound: true }) - } -}) -//PATCH-Method, updates a thought when liked. +// Updates a thought when liked. app.patch("/thoughts/:id", async (req, res) => { try { const id = req.params.id @@ -237,9 +233,9 @@ app.patch("/thoughts/:id", async (req, res) => { } catch (error) { res.status(500).json({ error: "Could not update thought" }) } -}) +}); -//DELETE-method, deletes a thought +// Deletes a thought app.delete("/thoughts/:id", async (req, res) => { try { const id = req.params.id @@ -252,28 +248,12 @@ app.delete("/thoughts/:id", async (req, res) => { } catch (error) { res.status(500).json({ error: "Could not delete thought " }) } -}) - -// GET-method, secret endpoint. -app.get("/secrets", authenticateUser); -app.get("/secrets", (req, res) => { - res.json({ secret: "This is a super secret message." }) -}) - -// Log-in endpoint. Finds user. -app.post("/sessions", async () => { - const user = await User.findOne({ email: req.body.email }); - if (user && bcrypt.compareSync(req.body.password, user.password)) { - res.json({ userId: user._id, accessToken: user.accessToken }); - } else { - res.json({ notFound: true }); - } }); // Start the server const server = app.listen(port, () => { console.log(`Server running on http://localhost:${port}`) -}) +}); // Graceful shutdown for Nodemon process.on('SIGTERM', () => { @@ -282,4 +262,4 @@ process.on('SIGTERM', () => { mongoose.connection.close() process.exit(0) }) -}) \ No newline at end of file +}); \ No newline at end of file From 323c845fb5f3edc744019dd81b881a1b9d348ff3 Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Tue, 3 Feb 2026 15:27:39 +0100 Subject: [PATCH 12/19] adding new files and folders --- middleware/authMiddleware.js | 0 schema/Thoughts.js | 0 schema/User.js | 0 server.js | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 middleware/authMiddleware.js create mode 100644 schema/Thoughts.js create mode 100644 schema/User.js diff --git a/middleware/authMiddleware.js b/middleware/authMiddleware.js new file mode 100644 index 0000000..e69de29 diff --git a/schema/Thoughts.js b/schema/Thoughts.js new file mode 100644 index 0000000..e69de29 diff --git a/schema/User.js b/schema/User.js new file mode 100644 index 0000000..e69de29 diff --git a/server.js b/server.js index 1ba3adc..bdbcf59 100644 --- a/server.js +++ b/server.js @@ -110,7 +110,7 @@ app.post("/users", async (req, res) => { } }); -// POST-method. Log-in endpoint. Finds user. +// Log-in endpoint. Finds user. app.post("/sessions", async (req, res) => { const user = await User.findOne({ email: req.body.email }); if (user && bcrypt.compareSync(req.body.password, user.password)) { From 87bf7f0d74bbf9e0ea44f34d1d25db3784b5631e Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Tue, 3 Feb 2026 17:06:28 +0100 Subject: [PATCH 13/19] restructuring files and folders --- middleware/authMiddleware.js | 15 +++ routes/thoughtRoutes.js | 119 +++++++++++++++++ routes/userRoutes.js | 93 ++++++++++++++ schema/Thoughts.js | 18 +++ schema/User.js | 25 ++++ server.js | 240 ++--------------------------------- 6 files changed, 284 insertions(+), 226 deletions(-) create mode 100644 routes/thoughtRoutes.js create mode 100644 routes/userRoutes.js diff --git a/middleware/authMiddleware.js b/middleware/authMiddleware.js index e69de29..bce0756 100644 --- a/middleware/authMiddleware.js +++ b/middleware/authMiddleware.js @@ -0,0 +1,15 @@ +import { User } from "../schema/User.js"; + +export const authenticateUser = async (req, res, next) => { + const user = await User.findOne({ + accessToken: req.header("Authorization") + }); + if (user) { + req.user = user + next(); + } else { + res.status(401).json({ + loggedOut: true + }); + } +}; diff --git a/routes/thoughtRoutes.js b/routes/thoughtRoutes.js new file mode 100644 index 0000000..cd6fe0c --- /dev/null +++ b/routes/thoughtRoutes.js @@ -0,0 +1,119 @@ +import express from "express"; +import { Thought } from "../schema/Thoughts.js"; +import { authenticateUser } from "../middleware/authMiddleware.js"; + +export const router = express.Router(); + +// Endpoint for all the thoughts. +router.get("/thoughts", async (req, res) => { + try { + const thoughts = await Thought.find() + res.json(thoughts); + } catch (error) { + res.status(500).json({ error: "Could not fetch thoughts" }) + } +}); + +// Endpoint for the thoughts id, to get one specific thought. +router.get("/thoughts/:id", async (req, res) => { + try { + const thoughtsId = await Thought.findById(req.params.id) + + if (!thoughtsId) { + return res.status(404).json({ error: `Thought with id ${req.params.id} does not exist` }) + } + res.json(thoughtsId) + + } catch (error) { + return res.status(500).json({ error: `Could not fetch thoughts` }) + } +}); + +// Adding a new message to the database +router.post("/thoughts", async (req, res) => { + try { + const { message } = req.body + + if (!message || message.trim().length === 0) { + return res.status(400).json({ error: `Message is required` }) + } + + const newThought = await Thought.create({ message }) + + res.status(201).json(newThought) + } catch (error) { + res.status(500).json({ error: `Could not create thought` }) + } +}); + +// Endpoint for liking a thought, increases hearts by 1 +router.post("/thoughts/:id/like", async (req, res) => { + try { + const id = req.params.id; + const thought = await Thought.findById(id); + + if (!thought) { + return res.status(404).json({ error: "Thought not found" }); + } + thought.hearts += 1; + await thought.save(); + res.json(thought); + + } catch (error) { + res.status(500).json({ error: "Could not like thought" }); + } +}); + + +// Updates a thought - can update message and/or hearts +router.patch("/thoughts/:id", async (req, res) => { + try { + const id = req.params.id + const { message, hearts } = req.body + + const update = {} + + if (message !== undefined) { + if (message.trim().length === 0) { + return res.status(400).json({ error: "Message can not be empty" }) + } + update.message = message + } + + if (hearts !== undefined) { + if (isNaN(hearts)) { + return res.status(400).json({ error: "Hearts must be a number" }) + } + update.hearts = hearts + } + + const updatedThought = await Thought.findByIdAndUpdate( + id, + update, + { new: true } + ) + + if (!updatedThought) { + return res.status(404).json({ error: "Thought not found" }) + } + + res.json(updatedThought) + } catch (error) { + res.status(500).json({ error: "Could not update thought" }) + } +}); + +// Deletes a thought +router.delete("/thoughts/:id", async (req, res) => { + try { + const id = req.params.id + const deletedThought = await Thought.findByIdAndDelete(id) + + if (!deletedThought) { + return res.status(404).json({ error: `Thought with id ${id} does not exist` }) + } + res.json(deletedThought) + } catch (error) { + res.status(500).json({ error: "Could not delete thought " }) + } +}); \ No newline at end of file diff --git a/routes/userRoutes.js b/routes/userRoutes.js new file mode 100644 index 0000000..bff38e1 --- /dev/null +++ b/routes/userRoutes.js @@ -0,0 +1,93 @@ +import express from "express"; +import bcrypt from "bcrypt"; +import { User } from "../schema/User.js"; +import { authenticateUser } from "../middleware/authMiddleware.js"; + +export const router = express.Router(); + +// Creates a new user. Registration endpoint. +router.post("/users", async (req, res) => { + try { + const { email, password } = req.body; + + if (!email || !password) { + return res.status(400).json({ + success: false, + message: "Email and password are required" + }); + } + + const existingUser = await User.findOne({ email: email.toLowerCase() }) + + if (existingUser) { + return res.status(400).json({ + success: false, + message: "Invalid email or password", + }); + } + + const salt = bcrypt.genSaltSync(10) // 10 making it harder to hack the password. + const hashedPassword = bcrypt.hashSync(password, salt) + const user = new User({ email, password: hashedPassword }); + + await user.save(); + res.status(201).json({ + success: true, + message: "User created", + response: { + email: user.email, + id: user._id, + accessToken: user.accessToken, + } + }); + + } catch (error) { + res.status(400).json({ + success: false, + message: "Could not create user", + response: error, + }); + } +}); + +// Log-in endpoint. Finds user. +router.post("/sessions", async (req, res) => { + try { + const { email, password } = req.body; + + if (!email || !password) { + return res.status(400).json({ + success: false, + message: "Email and password are required", + }); + } + + const user = await User.findOne({ email: email.toLowerCase() }); + + if (!user || !bcrypt.compareSync(password, user.password)) { + return res.status(401).json({ + success: false, + message: "Invalid email or password", + }) + } + res.json({ + success: true, + message: "Login successfull", + response: { + email: user.email, + id: user._id, + accessToken: user.accessToken + } + }); + } catch (error) { + res.status(500).json({ + success: false, + message: "Something went wrong", + }); + } +}); + +// ======= Protected Routes ======= +router.get("/secrets", authenticateUser, (req, res) => { + res.json({ secret: "This is a super secret message." }) +}); diff --git a/schema/Thoughts.js b/schema/Thoughts.js index e69de29..feef293 100644 --- a/schema/Thoughts.js +++ b/schema/Thoughts.js @@ -0,0 +1,18 @@ +import { Schema, model } from "mongoose"; + +const thoughtSchema = new Schema({ + message: { + type: String, + required: true + }, + hearts: { + type: Number, + default: 0, + }, + createdAt: { + type: Date, + default: () => new Date() + } +}) + +export const Thought = model("thought", thoughtSchema); \ No newline at end of file diff --git a/schema/User.js b/schema/User.js index e69de29..dd514f3 100644 --- a/schema/User.js +++ b/schema/User.js @@ -0,0 +1,25 @@ +import { Schema, model } from "mongoose"; +import crypto from "crypto"; + +const userSchema = new Schema({ + name: { + type: String, + unique: true, + required: true, + }, + email: { + type: String, + unique: true, + required: true, + }, + password: { + type: String, + required: true + }, + accessToken: { + type: String, + default: () => crypto.randomBytes(128).toString("hex") + } +}); + +export const User = model("User", userSchema); \ No newline at end of file diff --git a/server.js b/server.js index bdbcf59..68071b7 100644 --- a/server.js +++ b/server.js @@ -1,131 +1,18 @@ import cors from "cors"; import express from "express"; -import listEndpoints from "express-list-endpoints"; import mongoose from "mongoose"; import "dotenv/config"; -import crypto from "crypto"; -import bcrypt from "bcrypt"; +import listEndpoints from "express-list-endpoints"; +import { router as thoughtRouter } from "./routes/thoughtRoutes.js"; +import { router as userRouter } from "./routes/userRoutes.js"; // ======= Config & setup ======= const port = process.env.PORT || 9090 const app = express() -// Add middlewares to enable cors and json body parsing app.use(cors()) app.use(express.json()) -// Error if the database is not responding. Also a middleware. -app.use((req, res, next) => { - if (mongoose.connection.readyState === 1) { - next() - } else { - res.status(503).json({ error: `Server unavailable` }) - } -}); - -// ======= Database connection ======= -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/auth" -mongoose.connect(mongoUrl); -mongoose.Promise = Promise - -// ======= Schemas & Models ======= -const userSchema = new mongoose.Schema({ - name: { - type: String, - unique: true - }, - email: { - type: String, - unique: true, - }, - password: { - type: String, - required: true - }, - accessToken: { - type: String, - default: () => crypto.randomBytes(128).toString("hex") - } -}); - -const thoughtSchema = new mongoose.Schema({ - message: { - type: String, - required: true - }, - hearts: { - type: Number, - default: 0, - }, - createdAt: { - type: Date, - default: () => new Date() - } -}) - -const User = mongoose.model("User", userSchema); -const Thought = mongoose.model("thought", thoughtSchema); - -// ======= Middleware-function ======= -const authenticateUser = async (req, res, next) => { - const user = await User.findOne({ - accessToken: req.header("Authorization") - }); - if (user) { - req.user = user - next(); - } else { - res.status(401).json({ - loggedOut: true - }); - } -}; - -// ======= Authentication routes (Login/Signup) ======= -// Creates a new user. Registration endpoint. -app.post("/users", async (req, res) => { - try { - const { name, email, password } = req.body; - - if (!name || !email || !password) { - return res.status(400).json({ error: "Name, email and password are required" }); - } - - const salt = bcrypt.genSaltSync() - const user = new User({ name, email, password: bcrypt.hashSync(password, salt) }); - await user.save(); - res.status(201).json({ - success: true, - message: "User created", - id: user._id, - accessToken: user.accessToken - }); - } catch (error) { - console.log(error); - res.status(400).json({ - success: false, - message: "Could not create user", - errors: error - }); - } -}); - -// Log-in endpoint. Finds user. -app.post("/sessions", async (req, res) => { - const user = await User.findOne({ email: req.body.email }); - if (user && bcrypt.compareSync(req.body.password, user.password)) { - res.json({ userId: user._id, accessToken: user.accessToken }); - } else { - res.json({ notFound: true }); - } -}); - -// ======= Protected Routes ======= -app.get("/secrets", authenticateUser, (req, res) => { - res.json({ secret: "This is a super secret message." }) -}); - -// ======= Thoughts Routes ======= // Showing all the endpoints and documentation. app.get("/", (req, res) => { const endpoints = listEndpoints(app); @@ -136,119 +23,20 @@ app.get("/", (req, res) => { }]) }); -// Endpoint for all the thoughts. -app.get("/thoughts", async (req, res) => { - try { - const thoughts = await Thought.find() - res.json(thoughts); - } catch (error) { - res.status(500).json({ error: "Could not fetch thoughts" }) - } -}); - -// Endpoint for the thoughts id, to get one specific thought. -app.get("/thoughts/:id", async (req, res) => { - try { - const thoughtsId = await Thought.findById(req.params.id) - - if (!thoughtsId) { - return res.status(404).json({ error: `Thought with id ${req.params.id} does not exist` }) - } - res.json(thoughtsId) - - } catch (error) { - return res.status(500).json({ error: `Could not fetch thoughts` }) - } -}); - -// Adding a new message to the database -app.post("/thoughts", async (req, res) => { - try { - const { message } = req.body - - if (!message || message.trim().length === 0) { - return res.status(400).json({ error: `Message is required` }) - } - - const newThought = await Thought.create({ message }) - - res.status(201).json(newThought) - } catch (error) { - res.status(500).json({ error: `Could not create thought` }) - } -}); - -// Endpoint for liking a thought -app.post("/thoughts/:id/like", async (req, res) => { - try { - const id = req.params.id; - const thought = await Thought.findById(id); - - if (!thought) { - return res.status(404).json({ error: "Thought not found" }); - } - thought.hearts += 1; - await thought.save(); - res.json(thought); - - } catch (error) { - res.status(500).json({ error: "Could not like thought" }); - } -}); - - -// Updates a thought when liked. -app.patch("/thoughts/:id", async (req, res) => { - try { - const id = req.params.id - const { message, hearts } = req.body - - const update = {} - - if (message !== undefined) { - if (message.trim().length === 0) { - return res.status(400).json({ error: "Message can not be empty" }) - } - update.message = message - } - - if (hearts !== undefined) { - if (isNaN(hearts)) { - return res.status(400).json({ error: "Hearts must be a number" }) - } - update.hearts = hearts - } - - const updatedThought = await Thought.findByIdAndUpdate( - id, - update, - { new: true } - ) - - if (!updatedThought) { - return res.status(404).json({ error: "Thought not found" }) - } +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/auth" +mongoose.connect(mongoUrl); - res.json(updatedThought) - } catch (error) { - res.status(500).json({ error: "Could not update thought" }) +app.use((req, res, next) => { + if (mongoose.connection.readyState === 1) { + next() + } else { + res.status(503).json({ error: "Server is unavailable" }) } -}); - -// Deletes a thought -app.delete("/thoughts/:id", async (req, res) => { - try { - const id = req.params.id - const deletedThought = await Thought.findByIdAndDelete(id) +}) - if (!deletedThought) { - return res.status(404).json({ error: `Thought with id ${id} does not exist` }) - } - res.json(deletedThought) - } catch (error) { - res.status(500).json({ error: "Could not delete thought " }) - } -}); +// The connections to the different routes with endpoints +app.use(userRouter); +app.use(thoughtRouter); // Start the server const server = app.listen(port, () => { From 4d9ffaacff6ce0bd58a92225b4c2b78410da2238 Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Wed, 4 Feb 2026 08:57:31 +0100 Subject: [PATCH 14/19] debugging signup --- routes/userRoutes.js | 4 ++-- schema/User.js | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index bff38e1..3555267 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -5,7 +5,7 @@ import { authenticateUser } from "../middleware/authMiddleware.js"; export const router = express.Router(); -// Creates a new user. Registration endpoint. +// Creates a new user. Sign-up router.post("/users", async (req, res) => { try { const { email, password } = req.body; @@ -50,7 +50,7 @@ router.post("/users", async (req, res) => { } }); -// Log-in endpoint. Finds user. +// Log-in endpoint. Finds user that has created an account. router.post("/sessions", async (req, res) => { try { const { email, password } = req.body; diff --git a/schema/User.js b/schema/User.js index dd514f3..ce65bf8 100644 --- a/schema/User.js +++ b/schema/User.js @@ -2,11 +2,6 @@ import { Schema, model } from "mongoose"; import crypto from "crypto"; const userSchema = new Schema({ - name: { - type: String, - unique: true, - required: true, - }, email: { type: String, unique: true, From 89ba4b00c29a3f1fd9f5d8a81af58e02f095bb1c Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Wed, 4 Feb 2026 12:08:02 +0100 Subject: [PATCH 15/19] adding authentication --- routes/thoughtRoutes.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routes/thoughtRoutes.js b/routes/thoughtRoutes.js index cd6fe0c..b5da847 100644 --- a/routes/thoughtRoutes.js +++ b/routes/thoughtRoutes.js @@ -30,7 +30,7 @@ router.get("/thoughts/:id", async (req, res) => { }); // Adding a new message to the database -router.post("/thoughts", async (req, res) => { +router.post("/thoughts", authenticateUser, async (req, res) => { try { const { message } = req.body @@ -66,7 +66,7 @@ router.post("/thoughts/:id/like", async (req, res) => { // Updates a thought - can update message and/or hearts -router.patch("/thoughts/:id", async (req, res) => { +router.patch("/thoughts/:id", authenticateUser, async (req, res) => { try { const id = req.params.id const { message, hearts } = req.body @@ -104,7 +104,7 @@ router.patch("/thoughts/:id", async (req, res) => { }); // Deletes a thought -router.delete("/thoughts/:id", async (req, res) => { +router.delete("/thoughts/:id", authenticateUser, async (req, res) => { try { const id = req.params.id const deletedThought = await Thought.findByIdAndDelete(id) From b942b8d215fa4172fc441dac6f59943ae2d463f9 Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Wed, 4 Feb 2026 17:52:26 +0100 Subject: [PATCH 16/19] small fixes, adding comments etc --- server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server.js b/server.js index 68071b7..52d1757 100644 --- a/server.js +++ b/server.js @@ -23,6 +23,7 @@ app.get("/", (req, res) => { }]) }); +// Database connection const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/auth" mongoose.connect(mongoUrl); From b9af4eba5124dc8e0a11fafd92a23b7d986c5ad1 Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Thu, 5 Feb 2026 12:20:19 +0100 Subject: [PATCH 17/19] small fixes --- routes/userRoutes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 3555267..fada9a7 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -72,7 +72,7 @@ router.post("/sessions", async (req, res) => { } res.json({ success: true, - message: "Login successfull", + message: "Login successful", response: { email: user.email, id: user._id, @@ -87,7 +87,7 @@ router.post("/sessions", async (req, res) => { } }); -// ======= Protected Routes ======= +// ======= Protected Routes - not in use ======= router.get("/secrets", authenticateUser, (req, res) => { res.json({ secret: "This is a super secret message." }) }); From 39e39fc29504e8cc2091dddf5253680d6d5737d1 Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Fri, 6 Feb 2026 10:13:40 +0100 Subject: [PATCH 18/19] removing auth_base_url, to only use BASE_URL everywhere --- routes/userRoutes.js | 4 ++-- server.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index fada9a7..f7ff2ad 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -6,7 +6,7 @@ import { authenticateUser } from "../middleware/authMiddleware.js"; export const router = express.Router(); // Creates a new user. Sign-up -router.post("/users", async (req, res) => { +router.post("/signup", async (req, res) => { try { const { email, password } = req.body; @@ -51,7 +51,7 @@ router.post("/users", async (req, res) => { }); // Log-in endpoint. Finds user that has created an account. -router.post("/sessions", async (req, res) => { +router.post("/login", async (req, res) => { try { const { email, password } = req.body; diff --git a/server.js b/server.js index 52d1757..48e7dc4 100644 --- a/server.js +++ b/server.js @@ -24,7 +24,7 @@ app.get("/", (req, res) => { }); // Database connection -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/auth" +const mongoUrl = process.env.MONGO_URL mongoose.connect(mongoUrl); app.use((req, res, next) => { From a3a2f321d7a44bc3564c9811d21acaade3a83a4f Mon Sep 17 00:00:00 2001 From: Marina Lendt Date: Fri, 6 Feb 2026 12:13:48 +0100 Subject: [PATCH 19/19] adding text to readme file --- README.md | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0f9f073..a084e8b 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,43 @@ # Project API -This project includes the packages and babel setup for an express server, and is just meant to make things a little simpler to get up and running with. +Render: https://js-project-api-e8xy.onrender.com +Netlify: https://project-happy-thoughts-ml.netlify.app/ -## Getting started +Welcome to my first backend project! A RESTful API for sharing and liking thoughts with user authentication. -Install dependencies with `npm install`, then start the server by running `npm run dev` +## Features -## View it live +- User Authentication** - Sign up and log in with email/password +- Create Thoughts** - Share your thoughts (5-140 characters) +- Like Thoughts** - Increase heart count on any thought +- Update Thoughts** - Edit your own thoughts +- Delete Thoughts** - Remove your own thoughts +- Password Encryption** - Bcrypt for secure password storage -Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. +## Tech Stack + +Backend: +- Node.js +- Express.js +Database: +- MongoDB with Mongoose +Authentication: +- access tokens +Security: +- Bcrypt password hashing +- CORS + +## API Endpoints +Authentication endpoints: +- POST /signup- Create new account +- POST /login - Log in to existing account + +Thoughts endpoints: +- GET /thoughts - Get all thoughts +- GET /thoughts/:id - Get single thought +- POST /thoughts - Create thought (authenticated) +- PATCH /thoughts/:id - Update thought (authenticated) +- DELETE /thoughts/:id - Delete thought (authenticated) +- POST /thoughts/:id/like - Like a thought + +# ENJOY # \ No newline at end of file