From d00cddec87f23294abb297a24569cb0173403659 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 20 Jan 2026 15:20:29 +0100 Subject: [PATCH 01/24] Add GET endpoints for thoughts and API documentation --- package.json | 19 +++++++++++++++---- server.js | 23 +++++++++++++++++++++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index bf25bb6..e93d02b 100644 --- a/package.json +++ b/package.json @@ -2,18 +2,29 @@ "name": "project-api", "version": "1.0.0", "description": "Project API", + "homepage": "https://github.com/JeffieJansson/ht-project-api#readme", + "bugs": { + "url": "https://github.com/JeffieJansson/ht-project-api/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/JeffieJansson/ht-project-api.git" + }, + "license": "ISC", + "author": "", + "type": "commonjs", + "main": "server.js", "scripts": { "start": "babel-node server.js", "dev": "nodemon server.js --exec babel-node" }, - "author": "", - "license": "ISC", "dependencies": { "@babel/core": "^7.17.9", "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", "cors": "^2.8.5", - "express": "^4.17.3", - "nodemon": "^3.0.1" + "express": "^4.22.1", + "express-list-endpoints": "^7.1.1", + "nodemon": "^3.1.11" } } diff --git a/server.js b/server.js index f47771b..6927b1a 100644 --- a/server.js +++ b/server.js @@ -1,10 +1,13 @@ 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 || 9000 const app = express() // Add middlewares to enable cors and json body parsing @@ -13,7 +16,23 @@ app.use(express.json()) // Start defining your routes here app.get("/", (req, res) => { - res.send("Hello Technigo!") + const endpoints = listEndpoints(app); + + res.json([{ + message: "Welcome to my Thoughts API", + endpoints: endpoints + }]); +}); + + +app.get("/thoughts", (req, res) => { + res.json(data) +}) + +app.get("/thoughts/:id", (req, res) => { + const { id } = req.params + const thoughtsId = data.find((thoughts) => thoughts._id === id) + res.json(thoughtsId) }) // Start the server From a0175a72d0b8d639ddbd396040c799f2985fb45b Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Wed, 21 Jan 2026 11:48:55 +0100 Subject: [PATCH 02/24] add filter of hearts to the endpoint which returns all thoughts --- server.js | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/server.js b/server.js index 6927b1a..147ecdf 100644 --- a/server.js +++ b/server.js @@ -4,17 +4,14 @@ 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 || 9000 const app = express() -// Add middlewares to enable cors and json body parsing + app.use(cors()) app.use(express.json()) -// Start defining your routes here +// documentation of the API with express-list-endpoints app.get("/", (req, res) => { const endpoints = listEndpoints(app); @@ -24,17 +21,35 @@ app.get("/", (req, res) => { }]); }); - +// One endpoint to return a collection of results(GET all thoughts) app.get("/thoughts", (req, res) => { - res.json(data) + const { hearts } = req.query; // Get the hearts using query parameter + let filteredData = data; + + // Filter hearts greater than or equal to the specified value to the endpoint which returns all thoughts + if (hearts) { + filteredData = filteredData.filter((thought) => { + return thought.hearts >= Number(hearts); + }); + } + + res.json(filteredData); }) + +// One endpoint to return a single result (GET thoughts by id) app.get("/thoughts/:id", (req, res) => { const { id } = req.params const thoughtsId = data.find((thoughts) => thoughts._id === id) + + if(!thoughtsId) { + return res.status(404).json({ error: `Thought with id ${id} not found` }); + } res.json(thoughtsId) }) + + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`) From 8067ba20126055c75fffa3c33414a70da3f5c7c1 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Mon, 26 Jan 2026 16:56:53 +0100 Subject: [PATCH 03/24] installed and imported dotenv --- package.json | 3 +++ server.js | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e93d02b..767f98b 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,11 @@ "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", "cors": "^2.8.5", + "dotenv": "^17.2.3", "express": "^4.22.1", "express-list-endpoints": "^7.1.1", + "mongodb": "^7.0.0", + "mongoose": "^9.1.5", "nodemon": "^3.1.11" } } diff --git a/server.js b/server.js index 147ecdf..7a0f4e7 100644 --- a/server.js +++ b/server.js @@ -1,8 +1,12 @@ import cors from "cors" import express from "express" +import mongoose from "mongoose" import data from "./data.json" with { type: "json" } import listEndpoints from "express-list-endpoints"; +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost:27017/thoughts-api" +mongoose.connect(mongoUrl) +mongoose.Promise = Promise const port = process.env.PORT || 9000 const app = express() @@ -53,4 +57,4 @@ app.get("/thoughts/:id", (req, res) => { // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`) -}) +}) \ No newline at end of file From 5d150e3b31cab975d0d34b9ae2a70d4d0c05d1c6 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 27 Jan 2026 19:09:36 +0100 Subject: [PATCH 04/24] import dotenv --- server.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server.js b/server.js index 7a0f4e7..934d9b1 100644 --- a/server.js +++ b/server.js @@ -3,6 +3,9 @@ import express from "express" import mongoose from "mongoose" import data from "./data.json" with { type: "json" } import listEndpoints from "express-list-endpoints"; +import dotenv from "dotenv"; + +dotenv.config(); const mongoUrl = process.env.MONGO_URL || "mongodb://localhost:27017/thoughts-api" mongoose.connect(mongoUrl) From 5ca9828960a0d879f095e1d4d657b7a0851410f1 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Wed, 28 Jan 2026 14:44:16 +0100 Subject: [PATCH 05/24] Import Mongoose, add thought schema, add seeding to db, change old code to mongoose query and find method on GET enpoints --- server.js | 107 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 19 deletions(-) diff --git a/server.js b/server.js index 934d9b1..fdaa6bf 100644 --- a/server.js +++ b/server.js @@ -1,27 +1,61 @@ import cors from "cors" -import express from "express" +import express, { response } from "express" import mongoose from "mongoose" -import data from "./data.json" with { type: "json" } import listEndpoints from "express-list-endpoints"; import dotenv from "dotenv"; dotenv.config(); -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost:27017/thoughts-api" +const mongoUrl = process.env.MONGO_URL //mongodb://localhost:27017/thoughtsdb mongoose.connect(mongoUrl) mongoose.Promise = Promise const port = process.env.PORT || 9000 const app = express() - +// Add middlewares to enable cors and json body parsing app.use(cors()) app.use(express.json()) + +// Thoughts schema +const ThoughtSchema = new mongoose.Schema({ + message: { + type: String, + required: [true, "A Message is required"], + minlength: [5, "A Message must be at least 5 characters"], + maxlength: [140, "A Message cannot exceed 140 characters"], + trim: true + }, + hearts: { + type: Number, + default: 0 + }, + createdAt: { + type: Date, + default: Date.now + } +}); + +const Thought = mongoose.model("Thought", ThoughtSchema); + +// Add seeding of db +if (process.env.RESET_DB) { + const seedDatabase = async () => { + await Thought.deleteMany(); + + thoughtData.forEach(thought => { + new Thought(thought).save(); + }); +} + seedDatabase(); +} + + // documentation of the API with express-list-endpoints app.get("/", (req, res) => { const endpoints = listEndpoints(app); - + console.log("mongo:", process.env.MONGO_URL); res.json([{ message: "Welcome to my Thoughts API", endpoints: endpoints @@ -29,33 +63,68 @@ app.get("/", (req, res) => { }); // One endpoint to return a collection of results(GET all thoughts) -app.get("/thoughts", (req, res) => { - const { hearts } = req.query; // Get the hearts using query parameter - let filteredData = data; +app.get("/thoughts", async (req, res) => { + const { hearts } = req.query; + + const query = {} - // Filter hearts greater than or equal to the specified value to the endpoint which returns all thoughts if (hearts) { - filteredData = filteredData.filter((thought) => { - return thought.hearts >= Number(hearts); + query.hearts = { $gte: Number(hearts) } //$gte - MongoDB operator for "greater than or equal to" + } + + try { + const filteredThoughts = await Thought.find(query) + if (filteredThoughts.length === 0) { + + return res.status(404).json({ + success: false, + response: [], + message: "No thoughts found for that query" }); } + return res.status(200).json({ + success: true, + response: filteredThoughts, + message: "Thoughts retrieved successfully" + }); + + } catch (error) { + return res.status(500).json({ + success: false, + response: [], + message: error, + }) + } +}); - res.json(filteredData); -}) // One endpoint to return a single result (GET thoughts by id) -app.get("/thoughts/:id", (req, res) => { +app.get("/thoughts/:id", async (req, res) => { const { id } = req.params - const thoughtsId = data.find((thoughts) => thoughts._id === id) + try { + const thought = await Thought.findById(Number(id)); - if(!thoughtsId) { - return res.status(404).json({ error: `Thought with id ${id} not found` }); + + if (!thought) { + + return res.status(404).json ({ + success: false, + response: [], + message: `Thought with id ${id} not found` + }) } - res.json(thoughtsId) -}) + } catch (error) { + return res.status(500).json ({ + success: false, + response: [], + message: error, + }) + } + +}) // Start the server app.listen(port, () => { From 4f0e56fc0a75485dd96b1a9cba170b1b90dbfdf0 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Wed, 28 Jan 2026 17:14:14 +0100 Subject: [PATCH 06/24] added post endpoint and success message --- server.js | 60 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/server.js b/server.js index fdaa6bf..e162e27 100644 --- a/server.js +++ b/server.js @@ -3,9 +3,13 @@ import express, { response } from "express" import mongoose from "mongoose" import listEndpoints from "express-list-endpoints"; import dotenv from "dotenv"; +import thoughtJson from "./data.json" with { type: "json" }; dotenv.config(); +// hacky way to load data.json +let thoughtData = thoughtJson; + const mongoUrl = process.env.MONGO_URL //mongodb://localhost:27017/thoughtsdb mongoose.connect(mongoUrl) mongoose.Promise = Promise @@ -44,10 +48,11 @@ if (process.env.RESET_DB) { const seedDatabase = async () => { await Thought.deleteMany(); - thoughtData.forEach(thought => { - new Thought(thought).save(); - }); -} + // Remove _id from each thought to let MongoDB generate new ones + const thoughtsToSeed = thoughtData.map(({ _id, __v, ...thought }) => thought); + + await Thought.insertMany(thoughtsToSeed); + } seedDatabase(); } @@ -101,9 +106,17 @@ app.get("/thoughts", async (req, res) => { // One endpoint to return a single result (GET thoughts by id) app.get("/thoughts/:id", async (req, res) => { - const { id } = req.params + const id = req.params.id try { - const thought = await Thought.findById(Number(id)); + 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) { @@ -111,10 +124,16 @@ app.get("/thoughts/:id", async (req, res) => { return res.status(404).json ({ success: false, response: [], - message: `Thought with id ${id} not found` + message: `Thought not found` }) } + return res.status(200).json ({ + success: true, + response: thought, + message: "Success" + }) + } catch (error) { return res.status(500).json ({ success: false, @@ -126,6 +145,33 @@ app.get("/thoughts/:id", async (req, res) => { }) +//create new thought and save() it to the database (POST) +app.post("/thoughts", async (req, res) => { + const body = req.body; + + try { + const newThought = { + message: body.message, + }; + + const createdThought = await new Thought(newThought).save() + + return res.status(201).json ({ + success: true, + response: createdThought, + message: "Thought created successfully" + }) + + } catch (error) { + return res.status(500).json ({ + success: false, + response: null, + message: error, + }) + } +}); + + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`) From e1749be440bcf1c644516142efa06e35fb6f4824 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Thu, 29 Jan 2026 09:21:32 +0100 Subject: [PATCH 07/24] add delete,update message and like enpoint with delete, patch and patch --- server.js | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/server.js b/server.js index e162e27..87dff52 100644 --- a/server.js +++ b/server.js @@ -171,6 +171,127 @@ app.post("/thoughts", async (req, res) => { } }); +//TODO delete a thought by id (DELETE) +app.delete("/thoughts/:id", async (req, res) => { + const id = req.params.id; + + try { + 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) { + 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) { + return res.status(500).json({ + success: false, + response: null, + message: error.message + }); + } +}) + +// Like a thought by id (PATCH) +app.patch("/thoughts/:id/like", async (req, res) => { + const id = req.params.id; + + try { + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).json ({ + success: false, + response: null, + message: "Invalid ID format" + }); + } + + const updatedThought = await Thought.findByIdAndUpdate( + id, + { $inc: { hearts: 1 } }, + { new: 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 liked successfully" + }); + + } catch (error) { + return res.status(500).json({ + success: false, + response: null, + message: error, + }); + } +}) + +// Update a thought by id (PATCH) +app.patch("/thoughts/:id", async (req, res) => { + const id = req.params.id; + const body = req.body; + + try { + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).json({ + success: false, + response: null, + message: "Invalid ID format" + }); + } + + const updatedThought = await Thought.findByIdAndUpdate( + id, + { message: body.message }, + { new: 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(400).json({ + success: false, + response: null, + message: error.message + }); + } +}) // Start the server app.listen(port, () => { From 4bb1ce5b587258a5b9f6aa2b2fd4e10b181af253 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Thu, 29 Jan 2026 11:45:38 +0100 Subject: [PATCH 08/24] Switch to MongoDB Atlas for database connection --- server.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index 87dff52..259f67e 100644 --- a/server.js +++ b/server.js @@ -7,10 +7,10 @@ import thoughtJson from "./data.json" with { type: "json" }; dotenv.config(); -// hacky way to load data.json +// Load initial data for seeding (only used if RESET_DB is true) let thoughtData = thoughtJson; -const mongoUrl = process.env.MONGO_URL //mongodb://localhost:27017/thoughtsdb +const mongoUrl = process.env.MONGO_URL mongoose.connect(mongoUrl) mongoose.Promise = Promise @@ -49,6 +49,7 @@ if (process.env.RESET_DB) { await Thought.deleteMany(); // Remove _id from each thought to let MongoDB generate new ones + // other way to do it is to use thoughtData = thoughtData.map(thought => { delete thought._id; return thought; }); const thoughtsToSeed = thoughtData.map(({ _id, __v, ...thought }) => thought); await Thought.insertMany(thoughtsToSeed); From 07d65654013b1859fa3954ed5d82684a31fcc0a0 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Thu, 29 Jan 2026 12:44:52 +0100 Subject: [PATCH 09/24] debugging loss of POST data when restart of server --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index 259f67e..1ecc238 100644 --- a/server.js +++ b/server.js @@ -44,7 +44,7 @@ const ThoughtSchema = new mongoose.Schema({ const Thought = mongoose.model("Thought", ThoughtSchema); // Add seeding of db -if (process.env.RESET_DB) { +if (process.env.RESET_DB === "true") { const seedDatabase = async () => { await Thought.deleteMany(); From 61bb6616bfa80840f59d0469e9ca72c76723bb0e Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Fri, 30 Jan 2026 12:05:28 +0100 Subject: [PATCH 10/24] add sorting to make new thought first in thought list --- server.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/server.js b/server.js index 1ecc238..1d4b506 100644 --- a/server.js +++ b/server.js @@ -71,29 +71,26 @@ app.get("/", (req, res) => { // One endpoint to return a collection of results(GET all thoughts) app.get("/thoughts", async (req, res) => { const { hearts } = req.query; - const query = {} if (hearts) { - query.hearts = { $gte: Number(hearts) } //$gte - MongoDB operator for "greater than or equal to" + query.hearts = { $gte: Number(hearts) } } try { - const filteredThoughts = await Thought.find(query) + const filteredThoughts = await Thought.find(query).sort({ createdAt: -1 }); if (filteredThoughts.length === 0) { - - return res.status(404).json({ - success: false, - response: [], - message: "No thoughts found for that query" - }); - } + return res.status(404).json({ + success: false, + response: [], + message: "No thoughts found for that query" + }); + } return res.status(200).json({ success: true, response: filteredThoughts, message: "Thoughts retrieved successfully" }); - } catch (error) { return res.status(500).json({ success: false, From bc6187d8bfe1ba365e79071af3f2058156b1d9e7 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 3 Feb 2026 12:57:51 +0100 Subject: [PATCH 11/24] update server to use route modules and models and moved routes into separate files --- server.js | 255 ++---------------------------------------------------- 1 file changed, 9 insertions(+), 246 deletions(-) diff --git a/server.js b/server.js index 1d4b506..9d0ea10 100644 --- a/server.js +++ b/server.js @@ -1,15 +1,19 @@ import cors from "cors" -import express, { response } from "express" +import express from "express" import mongoose from "mongoose" import listEndpoints from "express-list-endpoints"; import dotenv from "dotenv"; import thoughtJson from "./data.json" with { type: "json" }; +import Thought from "./models/Thought.js"; +import thoughtRoutes from "./routes/thoughtRoutes.js"; +import userRoutes from "./routes/userRoutes.js"; dotenv.config(); // Load initial data for seeding (only used if RESET_DB is true) let thoughtData = thoughtJson; +//Connecting to MongoDB const mongoUrl = process.env.MONGO_URL mongoose.connect(mongoUrl) mongoose.Promise = Promise @@ -22,27 +26,6 @@ app.use(cors()) app.use(express.json()) -// Thoughts schema -const ThoughtSchema = new mongoose.Schema({ - message: { - type: String, - required: [true, "A Message is required"], - minlength: [5, "A Message must be at least 5 characters"], - maxlength: [140, "A Message cannot exceed 140 characters"], - trim: true - }, - hearts: { - type: Number, - default: 0 - }, - createdAt: { - type: Date, - default: Date.now - } -}); - -const Thought = mongoose.model("Thought", ThoughtSchema); - // Add seeding of db if (process.env.RESET_DB === "true") { const seedDatabase = async () => { @@ -63,234 +46,14 @@ app.get("/", (req, res) => { const endpoints = listEndpoints(app); console.log("mongo:", process.env.MONGO_URL); res.json([{ - message: "Welcome to my Thoughts API", + message: "Welcome to the Thoughts API", endpoints: endpoints }]); }); -// One endpoint to return a collection of results(GET all thoughts) -app.get("/thoughts", async (req, res) => { - const { hearts } = req.query; - const query = {} - - if (hearts) { - query.hearts = { $gte: Number(hearts) } - } - - try { - const filteredThoughts = await Thought.find(query).sort({ createdAt: -1 }); - if (filteredThoughts.length === 0) { - return res.status(404).json({ - success: false, - response: [], - message: "No thoughts found for that query" - }); - } - return res.status(200).json({ - success: true, - response: filteredThoughts, - message: "Thoughts retrieved successfully" - }); - } catch (error) { - return res.status(500).json({ - success: false, - response: [], - message: error, - }) - } -}); - - - -// One endpoint to return a single result (GET thoughts by id) -app.get("/thoughts/:id", async (req, res) => { - const id = req.params.id - try { - 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: [], - message: `Thought not found` - }) - } - - return res.status(200).json ({ - success: true, - response: thought, - message: "Success" - }) - - } catch (error) { - return res.status(500).json ({ - success: false, - response: [], - message: error, - }) - - } - -}) - -//create new thought and save() it to the database (POST) -app.post("/thoughts", async (req, res) => { - const body = req.body; - - try { - const newThought = { - message: body.message, - }; - - const createdThought = await new Thought(newThought).save() - - return res.status(201).json ({ - success: true, - response: createdThought, - message: "Thought created successfully" - }) - - } catch (error) { - return res.status(500).json ({ - success: false, - response: null, - message: error, - }) - } -}); - -//TODO delete a thought by id (DELETE) -app.delete("/thoughts/:id", async (req, res) => { - const id = req.params.id; - - try { - 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) { - 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) { - return res.status(500).json({ - success: false, - response: null, - message: error.message - }); - } -}) - -// Like a thought by id (PATCH) -app.patch("/thoughts/:id/like", async (req, res) => { - const id = req.params.id; - - try { - if (!mongoose.Types.ObjectId.isValid(id)) { - return res.status(400).json ({ - success: false, - response: null, - message: "Invalid ID format" - }); - } - - const updatedThought = await Thought.findByIdAndUpdate( - id, - { $inc: { hearts: 1 } }, - { new: 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 liked successfully" - }); - - } catch (error) { - return res.status(500).json({ - success: false, - response: null, - message: error, - }); - } -}) - -// Update a thought by id (PATCH) -app.patch("/thoughts/:id", async (req, res) => { - const id = req.params.id; - const body = req.body; - - try { - if (!mongoose.Types.ObjectId.isValid(id)) { - return res.status(400).json({ - success: false, - response: null, - message: "Invalid ID format" - }); - } - - const updatedThought = await Thought.findByIdAndUpdate( - id, - { message: body.message }, - { new: 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(400).json({ - success: false, - response: null, - message: error.message - }); - } -}) - +// These are the connections to the different routes with endpoints +app.use("/users", userRoutes); +app.use("/thoughts", thoughtRoutes); // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`) From ea8d0b8b9705a899d0378e7cbc77662ab4e5c875 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 3 Feb 2026 13:00:08 +0100 Subject: [PATCH 12/24] created separate file and added auth middleware --- middleware/authMiddleware.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 middleware/authMiddleware.js diff --git a/middleware/authMiddleware.js b/middleware/authMiddleware.js new file mode 100644 index 0000000..c3a126d --- /dev/null +++ b/middleware/authMiddleware.js @@ -0,0 +1,28 @@ +import User from "../models/User.js"; + +const authenticateUser = async (req, res, next) => { + try { + + // TODO check if user is authenticated + // TODO if not, return 401 + // TODO if yes, continue to next middleware + const user = await User.findOne({ + accessToken: req.header("Authorization").replace("Bearer ", ""), + }); + + if (!user) { + req.user = user; + next(); + } else { + res.status(401).json({ + message: "Unauthorized" + }); + } + + } catch (err) { + res + .status(500) + .json({ message: "Internal server error", error: err.message }); + } +}; +export default authenticateUser; From 2c6c543d2d5adfb416441d59f78d5f231f4fefa1 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 3 Feb 2026 13:02:21 +0100 Subject: [PATCH 13/24] changed to routes and moved thoughtroutes to separate file --- routes/thoughtRoutes.js | 230 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 routes/thoughtRoutes.js diff --git a/routes/thoughtRoutes.js b/routes/thoughtRoutes.js new file mode 100644 index 0000000..5ae57b9 --- /dev/null +++ b/routes/thoughtRoutes.js @@ -0,0 +1,230 @@ +import express from "express"; +import mongoose from "mongoose"; +import Thought from "../models/Thought.js"; +import authenticateUser from "../middleware/authMiddleware.js"; + +const router = express.Router(); + +//Endpoint is /thoughts +// Get all thoughts +router.get("/", async (req, res) => { + const { hearts } = req.query; + const query = {}; + + if (hearts) { + query.hearts = { $gte: Number(hearts) }; // Filter thoughts with hearts greater than or equal to the specified value + } + + try { + const filteredThoughts = await Thought.find(query).sort({ createdAt: -1 }); // Sort by createdAt in descending order + + if (filteredThoughts.length === 0) { + return res.status(404).json({ + success: false, + response: [], + message: "No thoughts found for that query. Try another one!", + }); + } + return res.status(200).json({ + success: true, + response: filteredThoughts, + message: "Success", + }); + } catch (error) { + return res.status(500).json({ + success: false, + response: [], + message: "Failed to fetch thoughts", + }); + } +}); + +// get one thought based on id +router.get("/:id", async (req, res) => { + const { id } = req.params; + + try { + 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", + }); + } + + res.status(200).json({ + success: true, + response: thought, + }); + } catch (error) { + res.status(500).json({ + success: false, + response: error, + message: "Thought couldn't be found", + }); + } +}); + +//create new thought and save to db +router.post("/", authenticateUser, async (req, res) => { + const body = req.body; + + try { + const newThought = { + message: body.message, + }; + + const createdThought = await new Thought(newThought).save(); + + return res.status(201).json({ + success: true, + response: createdThought, + message: "Thought created successfully", + }); + } catch (error) { + if (error.name === "ValidationError") { + return res.status(400).json({ + success: false, + response: null, + message: error.message, + }); + } + + return res.status(500).json({ + success: false, + response: null, + message: "Error creating thought", + }); + } +}); + +// delete a thought by id (DELETE) +router.delete("/:id", authenticateUser, async (req, res) => { + const id = req.params.id; + + try { + 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) { + 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) { + return res.status(500).json({ + success: false, + response: null, + message: "Error deleting thought", + }); + } +}); + +// Like a thought by id (PATCH) +router.patch("/:id/like", async (req, res) => { + const id = req.params.id; + + try { + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).json({ + success: false, + response: null, + message: "Invalid ID format", + }); + } + + const updatedThought = await Thought.findByIdAndUpdate( + id, + { $inc: { hearts: 1 } }, + { new: 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 liked successfully", + }); + } catch (error) { + return res.status(500).json({ + success: false, + response: null, + message: "Error liking thought", + }); + } +}); + +// Update a thought by id (PATCH) +router.patch("/:id", authenticateUser, async (req, res) => { + const id = req.params.id; + const body = req.body; + + try { + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).json({ + success: false, + response: null, + message: "Invalid ID format", + }); + } + + const updatedThought = await Thought.findByIdAndUpdate( + id, + { message: body.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) { + res.status(500).json({ + success: false, + response: error, + message: "Error editing thought", + }); + } +}); + +export default router; From f600bd50cafd577430a170f47d4657b30048d3e1 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 3 Feb 2026 13:02:53 +0100 Subject: [PATCH 14/24] Added signup/login routes --- routes/userRoutes.js | 93 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 routes/userRoutes.js diff --git a/routes/userRoutes.js b/routes/userRoutes.js new file mode 100644 index 0000000..f97cfc6 --- /dev/null +++ b/routes/userRoutes.js @@ -0,0 +1,93 @@ +import express from "express"; +import bcrypt from "bcrypt"; +import User from "../models/User.js"; + +const router = express.Router(); + // TODO create a salt and hash the password with bcrypt + // TODO check if user exists + // TODO create user and save to db + // TODO return success message + +//endpoint is /user/signup +// Here we can create a new user +router.post("/signup", async (req, res) => { + try { + const { email, password } = req.body; + + const existingUser = await User.findOne({ email: email.toLowerCase() }); + + if (existingUser) { + return res.status(400).json({ + success: false, + message: "An error occurred when creating the user", + }); + } + + const salt = bcrypt.genSaltSync(); + 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 successfully", + response: { + email: user.email, + id: user._id, + accessToken: user.accessToken, + }, + }); + + } catch (error) { + res.status(400).json({ + success: false, + message: "Failed to create user", + response: error, + }); + } +}); + +// endpoint is /user/login +// Here we can verify email and password and return accessToken +router.post("/login", async (req, res) => { + try { + 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 }); + res.json ({ + success: true, + message: "Login successful", + response: { + email: user.email, + id: user._id, + accessToken: user.accessToken, + } + }) + + } else { + res.status(401).json({ + success: false, + message: "Invalid email or password", + response: null, + }); + } + + } catch (error) { + res.status(500).json({ + success: false, + message: "Something went wrong", + response: error, + }); + } +}); + // TODO check if user exists + // TODO compare password + // TODO return success message + // TODO handle invalid credentials + + +// export the router to be used in server.js +export default router; + From 2aed55c56fc14457b3d1ea19615ed3201628141e Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 3 Feb 2026 13:03:35 +0100 Subject: [PATCH 15/24] created new file and moved thought model to it --- models/Thought.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 models/Thought.js diff --git a/models/Thought.js b/models/Thought.js new file mode 100644 index 0000000..be58508 --- /dev/null +++ b/models/Thought.js @@ -0,0 +1,23 @@ +import mongoose, { Schema } from "mongoose"; + +const ThoughtSchema = new mongoose.Schema({ + message: { + type: String, + required: [true, "A Message is required"], + minlength: [5, "A Message must be at least 5 characters"], + maxlength: [140, "A Message cannot exceed 140 characters"], + trim: true, + }, + hearts: { + type: Number, + default: 0, + }, + createdAt: { + type: Date, + default: Date.now, + }, +}); + +const Thought = mongoose.model("Thought", ThoughtSchema); + +export default Thought; From c00a591b884e076525d11f35d5fff339d69a19b7 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 3 Feb 2026 13:04:43 +0100 Subject: [PATCH 16/24] created user model --- models/User.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 models/User.js diff --git a/models/User.js b/models/User.js new file mode 100644 index 0000000..2223f55 --- /dev/null +++ b/models/User.js @@ -0,0 +1,23 @@ +import mongoose, { Schema } from "mongoose"; +import crypto from "crypto"; + +const userSchema = new Schema({ + email: { + type: String, + required: true, + unique: true, + }, + password: { + type: String, + required: true, + }, + accessToken: { + type: String, + required: true, + default: () => crypto.randomBytes(128).toString("hex"), + }, +}) + +const User = mongoose.model("User", userSchema); + +export default User; From b49b5dd4d49d3c3256500d8d90a2827d415d7b8a Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 3 Feb 2026 13:05:07 +0100 Subject: [PATCH 17/24] install bcrypt --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 767f98b..6b528fe 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,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.22.1", From 6b3034f7c9227d3843c6ba8dabdf88c031c3b263 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 3 Feb 2026 13:21:30 +0100 Subject: [PATCH 18/24] update code implements that did not get commited from last commit --- middleware/authMiddleware.js | 13 ++++------- routes/userRoutes.js | 45 ++++++++++++++++++------------------ server.js | 2 +- 3 files changed, 27 insertions(+), 33 deletions(-) diff --git a/middleware/authMiddleware.js b/middleware/authMiddleware.js index c3a126d..de04619 100644 --- a/middleware/authMiddleware.js +++ b/middleware/authMiddleware.js @@ -2,23 +2,18 @@ import User from "../models/User.js"; const authenticateUser = async (req, res, next) => { try { - - // TODO check if user is authenticated - // TODO if not, return 401 - // TODO if yes, continue to next middleware const user = await User.findOne({ accessToken: req.header("Authorization").replace("Bearer ", ""), }); - - if (!user) { + if (user) { req.user = user; next(); } else { - res.status(401).json({ - message: "Unauthorized" + res.status(401).json({ + message: "Authentication missing or invalid.", + loggedOut: true, }); } - } catch (err) { res .status(500) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index f97cfc6..09b2d42 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -29,7 +29,7 @@ router.post("/signup", async (req, res) => { await user.save(); - res.status(201).json({ + res.status(200).json({ success: true, message: "User created successfully", response: { @@ -52,29 +52,28 @@ router.post("/signup", async (req, res) => { // Here we can verify email and password and return accessToken router.post("/login", async (req, res) => { try { - 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 }); - res.json ({ - success: true, - message: "Login successful", - response: { - email: user.email, - id: user._id, - accessToken: user.accessToken, - } - }) - - } else { - res.status(401).json({ - success: false, - message: "Invalid email or password", - response: null, - }); - } + const { email, password } = req.body; - } catch (error) { + const user = await User.findOne({ email: email.toLowerCase() }); + + if (user && bcrypt.compareSync(password, user.password)) { + res.json({ + success: true, + message: "Login successful", + response: { + email: user.email, + id: user._id, + accessToken: user.accessToken, + }, + }); + } else { + res.status(401).json({ + success: false, + message: "Invalid email or password", + response: null, + }); + } + } catch (error) { res.status(500).json({ success: false, message: "Something went wrong", diff --git a/server.js b/server.js index 9d0ea10..0163ab7 100644 --- a/server.js +++ b/server.js @@ -5,6 +5,7 @@ import listEndpoints from "express-list-endpoints"; import dotenv from "dotenv"; import thoughtJson from "./data.json" with { type: "json" }; import Thought from "./models/Thought.js"; + import thoughtRoutes from "./routes/thoughtRoutes.js"; import userRoutes from "./routes/userRoutes.js"; @@ -44,7 +45,6 @@ if (process.env.RESET_DB === "true") { // documentation of the API with express-list-endpoints app.get("/", (req, res) => { const endpoints = listEndpoints(app); - console.log("mongo:", process.env.MONGO_URL); res.json([{ message: "Welcome to the Thoughts API", endpoints: endpoints From f774ce6aa1d8708b3df30075f54e4b7a9ef5886a Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 3 Feb 2026 14:28:19 +0100 Subject: [PATCH 19/24] change route from users to user --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index 0163ab7..0620019 100644 --- a/server.js +++ b/server.js @@ -52,7 +52,7 @@ app.get("/", (req, res) => { }); // These are the connections to the different routes with endpoints -app.use("/users", userRoutes); +app.use("/user", userRoutes); app.use("/thoughts", thoughtRoutes); // Start the server app.listen(port, () => { From a559ef30874aad44e0c53c4a3276b9bcc2398dca Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 3 Feb 2026 21:40:47 +0100 Subject: [PATCH 20/24] add minlenght to passowrd for signup --- models/User.js | 1 + routes/userRoutes.js | 5 ----- server.js | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/models/User.js b/models/User.js index 2223f55..f729cd6 100644 --- a/models/User.js +++ b/models/User.js @@ -10,6 +10,7 @@ const userSchema = new Schema({ password: { type: String, required: true, + minlength: 5, }, accessToken: { type: String, diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 09b2d42..862fae6 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -81,11 +81,6 @@ router.post("/login", async (req, res) => { }); } }); - // TODO check if user exists - // TODO compare password - // TODO return success message - // TODO handle invalid credentials - // export the router to be used in server.js export default router; diff --git a/server.js b/server.js index 0620019..b24e0c9 100644 --- a/server.js +++ b/server.js @@ -51,7 +51,7 @@ app.get("/", (req, res) => { }]); }); -// These are the connections to the different routes with endpoints + app.use("/user", userRoutes); app.use("/thoughts", thoughtRoutes); // Start the server From cd89bc5a51a36b1c1ba866b1f82d32f7fd968084 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 3 Feb 2026 22:08:08 +0100 Subject: [PATCH 21/24] trying out password error --- routes/userRoutes.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index 862fae6..a000d26 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -3,23 +3,27 @@ import bcrypt from "bcrypt"; import User from "../models/User.js"; const router = express.Router(); - // TODO create a salt and hash the password with bcrypt - // TODO check if user exists - // TODO create user and save to db - // TODO return success message //endpoint is /user/signup // Here we can create a new user router.post("/signup", async (req, res) => { try { const { email, password } = req.body; + + if (!password || password.length < 6) { + return res.status(400).json({ + success: false, + message: "Password is required. Password must be at least 6 characters.", + }); + } + const existingUser = await User.findOne({ email: email.toLowerCase() }); if (existingUser) { return res.status(400).json({ success: false, - message: "An error occurred when creating the user", + message: "User with this email already exists", }); } From 92ff70920d3f53c2a8d4b9a4e4561435c72a3ef7 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Tue, 3 Feb 2026 22:32:48 +0100 Subject: [PATCH 22/24] add minimun 6 char to password for signup --- routes/userRoutes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/userRoutes.js b/routes/userRoutes.js index a000d26..526629c 100644 --- a/routes/userRoutes.js +++ b/routes/userRoutes.js @@ -13,7 +13,7 @@ router.post("/signup", async (req, res) => { if (!password || password.length < 6) { return res.status(400).json({ success: false, - message: "Password is required. Password must be at least 6 characters.", + message: "Password must be at least 6 characters.", }); } From ab487ba674f82c15d2ff5d3244fb8cfb5795f380 Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Wed, 4 Feb 2026 08:26:13 +0100 Subject: [PATCH 23/24] edit incorrect value and mongoose schema --- models/Thought.js | 2 +- models/User.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/models/Thought.js b/models/Thought.js index be58508..98f584c 100644 --- a/models/Thought.js +++ b/models/Thought.js @@ -1,6 +1,6 @@ import mongoose, { Schema } from "mongoose"; -const ThoughtSchema = new mongoose.Schema({ +const ThoughtSchema = new Schema({ message: { type: String, required: [true, "A Message is required"], diff --git a/models/User.js b/models/User.js index f729cd6..62b1b07 100644 --- a/models/User.js +++ b/models/User.js @@ -10,7 +10,7 @@ const userSchema = new Schema({ password: { type: String, required: true, - minlength: 5, + minlength: 6, }, accessToken: { type: String, From e605f022af703538875dab7f008865439a4f237b Mon Sep 17 00:00:00 2001 From: Jennifer Jansson Date: Fri, 6 Feb 2026 10:10:46 +0100 Subject: [PATCH 24/24] removing seed and import of hard coded data and add readme --- README.md | 74 ++++++++++++++++++++++++++++++++++++---- pull_request_template.md | 3 +- server.js | 23 +------------ 3 files changed, 71 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 0f9f073..9e0bd1e 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,73 @@ -# Project API +# Happy Thoughts 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. -## Getting started +## Project Overview -Install dependencies with `npm install`, then start the server by running `npm run dev` +Happy Thoughts API is a RESTful backend built with Node.js, Express, and MongoDB. It provides endpoints for creating, reading, updating, liking, and deleting "thoughts" (messages), as well as user authentication and registration. -## View it live +### Key Features -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. +- **Mongoose Schema Models:** All data is structured and validated using Mongoose models for both thoughts and users. This ensures consistent data and enforces rules such as required fields and minimum password length. +- **Password Security:** User passwords are securely hashed using bcrypt before being stored in the database. +- **Authentication:** The API uses access tokens to protect routes for creating, updating, and deleting thoughts. Only authenticated users can perform these actions. +- **Error Handling:** All routes include robust error handling and return clear status codes and messages for invalid input, authentication failures, and other errors. +- **RESTful Design:** The API follows RESTful principles, with clear separation of resources and HTTP methods for CRUD operations. +- **Filtering & Sorting:** Thoughts can be filtered by number of likes (hearts) and sorted by creation date. +- **Frontend Integration:** Designed to work seamlessly with a frontend application, supporting features like updating and deleting thoughts, user signup/login, and error feedback. + +--- + +## Project Requirements + +**Fulfilled requirements:** +- ✅ API documentation with Express List Endpoints +- ✅ Read all thoughts +- ✅ Read a single thought +- ✅ Like a thought +- ✅ Create a thought (authenticated) +- ✅ Update a thought (authenticated) +- ✅ Delete a thought (authenticated) +- ✅ Sign up (register user) +- ✅ Log in (user authentication) +- ✅ RESTful structure +- ✅ Clean code according to guidelines +- ✅ Uses Mongoose models +- ✅ Validates user input +- ✅ Unique email addresses +- ✅ Error handling and status codes +- ✅ Frontend supports Update/Delete/Signup/Login and error handling +- ✅ Passwords encrypted with bcrypt +- ✅ API deployed on Render +- ✅ Backend and frontend are synced +- ✅ Filtering thoughts by number of hearts (`/thoughts?hearts=5`) +- ✅ Sorting by date (newest first) +- ✅ API error messages displayed in frontend during registration +- ✅ Token stored in localStorage and sent in headers + +--- + +## API Endpoints + +- `GET /thoughts` – Get all thoughts (with filtering/sorting) +- `GET /thoughts/:id` – Get a single thought +- `POST /thoughts` – Create a thought (requires authentication) +- `PATCH /thoughts/:id/like` – Like a thought +- `PATCH /thoughts/:id` – Update a thought (requires authentication) +- `DELETE /thoughts/:id` – Delete a thought (requires authentication) +- `POST /user/signup` – Register user +- `POST /user/login` – Log in user + +--- + +## File structure +middleware/ +└──authMiddleware.js +models/ +├── Thought.js +└── User.js +routes/ +├── thoughtRoutes.js +└── userRoutes.js + +└── server,js +``` \ No newline at end of file diff --git a/pull_request_template.md b/pull_request_template.md index fb9fdc3..47e2dce 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -1 +1,2 @@ -Please include your Render link here. \ No newline at end of file +Please include your Render link here. +https://ht-api-ij7j.onrender.com/ \ No newline at end of file diff --git a/server.js b/server.js index b24e0c9..6f5d13a 100644 --- a/server.js +++ b/server.js @@ -3,17 +3,12 @@ import express from "express" import mongoose from "mongoose" import listEndpoints from "express-list-endpoints"; import dotenv from "dotenv"; -import thoughtJson from "./data.json" with { type: "json" }; -import Thought from "./models/Thought.js"; import thoughtRoutes from "./routes/thoughtRoutes.js"; import userRoutes from "./routes/userRoutes.js"; dotenv.config(); -// Load initial data for seeding (only used if RESET_DB is true) -let thoughtData = thoughtJson; - //Connecting to MongoDB const mongoUrl = process.env.MONGO_URL mongoose.connect(mongoUrl) @@ -26,22 +21,6 @@ const app = express() app.use(cors()) app.use(express.json()) - -// Add seeding of db -if (process.env.RESET_DB === "true") { - const seedDatabase = async () => { - await Thought.deleteMany(); - - // Remove _id from each thought to let MongoDB generate new ones - // other way to do it is to use thoughtData = thoughtData.map(thought => { delete thought._id; return thought; }); - const thoughtsToSeed = thoughtData.map(({ _id, __v, ...thought }) => thought); - - await Thought.insertMany(thoughtsToSeed); - } - seedDatabase(); -} - - // documentation of the API with express-list-endpoints app.get("/", (req, res) => { const endpoints = listEndpoints(app); @@ -51,9 +30,9 @@ app.get("/", (req, res) => { }]); }); - app.use("/user", userRoutes); app.use("/thoughts", thoughtRoutes); + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`)