From 5e897dfe914f1e7261d2caf2dd019099e2bca243 Mon Sep 17 00:00:00 2001 From: Talo Vargas Date: Fri, 30 May 2025 01:27:30 +0200 Subject: [PATCH 1/8] install dependencies --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index bf25bb6..70ca4d2 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,9 @@ "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", "cors": "^2.8.5", + "dotenv": "^16.5.0", "express": "^4.17.3", + "express-list-endpoints": "^7.1.1", "nodemon": "^3.0.1" } } From 5c503859c10224f758247fb2486513a9893f1a0a Mon Sep 17 00:00:00 2001 From: Talo Vargas Date: Fri, 30 May 2025 01:34:29 +0200 Subject: [PATCH 2/8] create data subfolder with thoughts.json containing data.json content --- data/thoughts.json | 121 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 data/thoughts.json diff --git a/data/thoughts.json b/data/thoughts.json new file mode 100644 index 0000000..a2c844f --- /dev/null +++ b/data/thoughts.json @@ -0,0 +1,121 @@ +[ + { + "_id": "682bab8c12155b00101732ce", + "message": "Berlin baby", + "hearts": 37, + "createdAt": "2025-05-19T22:07:08.999Z", + "__v": 0 + }, + { + "_id": "682e53cc4fddf50010bbe739", + "message": "My family!", + "hearts": 0, + "createdAt": "2025-05-22T22:29:32.232Z", + "__v": 0 + }, + { + "_id": "682e4f844fddf50010bbe738", + "message": "The smell of coffee in the morning....", + "hearts": 23, + "createdAt": "2025-05-22T22:11:16.075Z", + "__v": 0 + }, + { + "_id": "682e48bf4fddf50010bbe737", + "message": "Newly washed bedlinen, kids that sleeps through the night.. FINGERS CROSSED 🤞🏼\n", + "hearts": 6, + "createdAt": "2025-05-21T21:42:23.862Z", + "__v": 0 + }, + { + "_id": "682e45804fddf50010bbe736", + "message": "I am happy that I feel healthy and have energy again", + "hearts": 13, + "createdAt": "2025-05-21T21:28:32.196Z", + "__v": 0 + }, + { + "_id": "682e23fecf615800105107aa", + "message": "cold beer", + "hearts": 2, + "createdAt": "2025-05-21T19:05:34.113Z", + "__v": 0 + }, + { + "_id": "682e22aecf615800105107a9", + "message": "My friend is visiting this weekend! <3", + "hearts": 6, + "createdAt": "2025-05-21T18:59:58.121Z", + "__v": 0 + }, + { + "_id": "682cec1b17487d0010a298b6", + "message": "A god joke: \nWhy did the scarecrow win an award?\nBecause he was outstanding in his field!", + "hearts": 12, + "createdAt": "2025-05-20T20:54:51.082Z", + "__v": 0 + }, + { + "_id": "682cebbe17487d0010a298b5", + "message": "Tacos and tequila🌮🍹", + "hearts": 2, + "createdAt": "2025-05-19T20:53:18.899Z", + "__v": 0 + }, + { + "_id": "682ceb5617487d0010a298b4", + "message": "Netflix and late night ice-cream🍦", + "hearts": 1, + "createdAt": "2025-05-18T20:51:34.494Z", + "__v": 0 + }, + { + "_id": "682c99ba3bff2d0010f5d44e", + "message": "Summer is coming...", + "hearts": 2, + "createdAt": "2025-05-20T15:03:22.379Z", + "__v": 0 + }, + { + "_id": "682c706c951f7a0017130024", + "message": "Exercise? I thought you said extra fries! 🍟😂", + "hearts": 14, + "createdAt": "2025-05-20T12:07:08.185Z", + "__v": 0 + }, + { + "_id": "682c6fe1951f7a0017130023", + "message": "I’m on a seafood diet. I see food, and I eat it.", + "hearts": 4, + "createdAt": "2025-05-20T12:04:49.978Z", + "__v": 0 + }, + { + "_id": "682c6f0e951f7a0017130022", + "message": "Cute monkeys🐒", + "hearts": 2, + "createdAt": "2025-05-20T12:01:18.308Z", + "__v": 0 + }, + { + "_id": "682c6e65951f7a0017130021", + "message": "The weather is nice!", + "hearts": 0, + "createdAt": "2025-05-20T11:58:29.662Z", + "__v": 0 + }, + { + "_id": "682bfdb4270ca300105af221", + "message": "good vibes and good things", + "hearts": 3, + "createdAt": "2025-05-20T03:57:40.322Z", + "__v": 0 + }, + { + "_id": "682bab8c12155b00101732ce", + "message": "Berlin baby", + "hearts": 37, + "createdAt": "2025-05-19T22:07:08.999Z", + "__v": 0 + } +] \ No newline at end of file From ae46f2f347d01abed626edac2b00af9abe10299c Mon Sep 17 00:00:00 2001 From: Talo Vargas Date: Fri, 30 May 2025 01:36:17 +0200 Subject: [PATCH 3/8] install nodemon as dev dependency --- package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 70ca4d2..0b48595 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,9 @@ "cors": "^2.8.5", "dotenv": "^16.5.0", "express": "^4.17.3", - "express-list-endpoints": "^7.1.1", - "nodemon": "^3.0.1" + "express-list-endpoints": "^7.1.1" + }, + "devDependencies": { + "nodemon": "^3.1.10" } } From 1b8c8a73dc7f62067c05f6325864e30c8ce8b37b Mon Sep 17 00:00:00 2001 From: Talo Vargas Date: Fri, 30 May 2025 01:57:20 +0200 Subject: [PATCH 4/8] install cors, updating server.js, implementing 3 endpoints --- data.json | 121 ------------------------------------------------------ server.js | 51 +++++++++++++++++------ 2 files changed, 38 insertions(+), 134 deletions(-) delete mode 100644 data.json diff --git a/data.json b/data.json deleted file mode 100644 index a2c844f..0000000 --- a/data.json +++ /dev/null @@ -1,121 +0,0 @@ -[ - { - "_id": "682bab8c12155b00101732ce", - "message": "Berlin baby", - "hearts": 37, - "createdAt": "2025-05-19T22:07:08.999Z", - "__v": 0 - }, - { - "_id": "682e53cc4fddf50010bbe739", - "message": "My family!", - "hearts": 0, - "createdAt": "2025-05-22T22:29:32.232Z", - "__v": 0 - }, - { - "_id": "682e4f844fddf50010bbe738", - "message": "The smell of coffee in the morning....", - "hearts": 23, - "createdAt": "2025-05-22T22:11:16.075Z", - "__v": 0 - }, - { - "_id": "682e48bf4fddf50010bbe737", - "message": "Newly washed bedlinen, kids that sleeps through the night.. FINGERS CROSSED 🤞🏼\n", - "hearts": 6, - "createdAt": "2025-05-21T21:42:23.862Z", - "__v": 0 - }, - { - "_id": "682e45804fddf50010bbe736", - "message": "I am happy that I feel healthy and have energy again", - "hearts": 13, - "createdAt": "2025-05-21T21:28:32.196Z", - "__v": 0 - }, - { - "_id": "682e23fecf615800105107aa", - "message": "cold beer", - "hearts": 2, - "createdAt": "2025-05-21T19:05:34.113Z", - "__v": 0 - }, - { - "_id": "682e22aecf615800105107a9", - "message": "My friend is visiting this weekend! <3", - "hearts": 6, - "createdAt": "2025-05-21T18:59:58.121Z", - "__v": 0 - }, - { - "_id": "682cec1b17487d0010a298b6", - "message": "A god joke: \nWhy did the scarecrow win an award?\nBecause he was outstanding in his field!", - "hearts": 12, - "createdAt": "2025-05-20T20:54:51.082Z", - "__v": 0 - }, - { - "_id": "682cebbe17487d0010a298b5", - "message": "Tacos and tequila🌮🍹", - "hearts": 2, - "createdAt": "2025-05-19T20:53:18.899Z", - "__v": 0 - }, - { - "_id": "682ceb5617487d0010a298b4", - "message": "Netflix and late night ice-cream🍦", - "hearts": 1, - "createdAt": "2025-05-18T20:51:34.494Z", - "__v": 0 - }, - { - "_id": "682c99ba3bff2d0010f5d44e", - "message": "Summer is coming...", - "hearts": 2, - "createdAt": "2025-05-20T15:03:22.379Z", - "__v": 0 - }, - { - "_id": "682c706c951f7a0017130024", - "message": "Exercise? I thought you said extra fries! 🍟😂", - "hearts": 14, - "createdAt": "2025-05-20T12:07:08.185Z", - "__v": 0 - }, - { - "_id": "682c6fe1951f7a0017130023", - "message": "I’m on a seafood diet. I see food, and I eat it.", - "hearts": 4, - "createdAt": "2025-05-20T12:04:49.978Z", - "__v": 0 - }, - { - "_id": "682c6f0e951f7a0017130022", - "message": "Cute monkeys🐒", - "hearts": 2, - "createdAt": "2025-05-20T12:01:18.308Z", - "__v": 0 - }, - { - "_id": "682c6e65951f7a0017130021", - "message": "The weather is nice!", - "hearts": 0, - "createdAt": "2025-05-20T11:58:29.662Z", - "__v": 0 - }, - { - "_id": "682bfdb4270ca300105af221", - "message": "good vibes and good things", - "hearts": 3, - "createdAt": "2025-05-20T03:57:40.322Z", - "__v": 0 - }, - { - "_id": "682bab8c12155b00101732ce", - "message": "Berlin baby", - "hearts": 37, - "createdAt": "2025-05-19T22:07:08.999Z", - "__v": 0 - } -] \ No newline at end of file diff --git a/server.js b/server.js index f47771b..426984c 100644 --- a/server.js +++ b/server.js @@ -1,22 +1,47 @@ -import cors from "cors" -import express from "express" +import express from 'express' +import cors from 'cors' +import listEndpoints from 'express-list-endpoints' +import dotenv from 'dotenv' + +import thoughts from './data/thoughts.json' + +dotenv.config() -// 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 app = express() +const PORT = process.env.PORT || 8080 -// Add middlewares to enable cors and json body parsing +// Enable CORS and JSON body parsing app.use(cors()) app.use(express.json()) -// Start defining your routes here -app.get("/", (req, res) => { - res.send("Hello Technigo!") +// 1) API documentation & welcome message +app.get('/', (req, res) => { + res.json({ + message: 'Welcome to the Happy Thoughts API', + endpoints: listEndpoints(app) + }) }) -// Start the server -app.listen(port, () => { - console.log(`Server running on http://localhost:${port}`) +// 2) Collection endpoint: get all thoughts +app.get('/thoughts', (req, res) => { + res.status(200).json(thoughts) }) + +// 3) Single-item endpoint: get one thought by ID +app.get('/thoughts/:id', (req, res) => { + const { id } = req.params + const found = thoughts.find((t) => t._id === id) + + if (!found) { + return res + .status(404) + .json({ error: `Thought with ID '${id}' not found` }) + } + + res.status(200).json(found) +}) + +// Start the server +app.listen(PORT, () => { + console.log(`Server running on http://localhost:${PORT}`) +}) \ No newline at end of file From a27787fbcc0ee38cf9b6b120094b231bcaeba686 Mon Sep 17 00:00:00 2001 From: Talo Vargas Date: Fri, 30 May 2025 02:18:03 +0200 Subject: [PATCH 5/8] add filter by hearts --- server.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index 426984c..b103932 100644 --- a/server.js +++ b/server.js @@ -22,9 +22,17 @@ app.get('/', (req, res) => { }) }) -// 2) Collection endpoint: get all thoughts +// 2) Collection endpoint: get all thoughts with optional filter by hearts app.get('/thoughts', (req, res) => { - res.status(200).json(thoughts) + const { hearts } = req.query + let results = thoughts + + if (hearts) { // Filter by hearts if query parameter is provided + const h = Number(hearts) // Convert to number + results = results.filter((t) => t.hearts === h) // Filter thoughts by hearts count + } + + res.status(200).json(results) // Return the filtered or unfiltered thoughts }) // 3) Single-item endpoint: get one thought by ID From b5767b8bac159ef782725e71a2e6ae1d3f1143cb Mon Sep 17 00:00:00 2001 From: Talo Vargas Date: Mon, 9 Jun 2025 19:19:57 +0200 Subject: [PATCH 6/8] double check I configure Babel and nodemon for ES modules right --- .babelrc | 2 +- package.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.babelrc b/.babelrc index 1320b9a..8aa924d 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,3 @@ { "presets": ["@babel/preset-env"] -} +} \ No newline at end of file diff --git a/package.json b/package.json index 0b48595..288b3cb 100644 --- a/package.json +++ b/package.json @@ -9,15 +9,15 @@ "author": "", "license": "ISC", "dependencies": { - "@babel/core": "^7.17.9", - "@babel/node": "^7.16.8", - "@babel/preset-env": "^7.16.11", "cors": "^2.8.5", "dotenv": "^16.5.0", "express": "^4.17.3", "express-list-endpoints": "^7.1.1" }, "devDependencies": { + "@babel/core": "^7.27.4", + "@babel/node": "^7.27.1", + "@babel/preset-env": "^7.27.2", "nodemon": "^3.1.10" } } From dbe247b16099394678a9c06b0d3e40188b573ebb Mon Sep 17 00:00:00 2001 From: Talo Vargas Date: Mon, 9 Jun 2025 19:30:47 +0200 Subject: [PATCH 7/8] configure Mongoose connection and Thought model --- models/Thought.js | 22 +++++++++++++++++ package.json | 3 ++- server.js | 60 +++++++++++++++++++++++++++++------------------ 3 files changed, 61 insertions(+), 24 deletions(-) create mode 100644 models/Thought.js diff --git a/models/Thought.js b/models/Thought.js new file mode 100644 index 0000000..ec2bd8f --- /dev/null +++ b/models/Thought.js @@ -0,0 +1,22 @@ +import mongoose from 'mongoose' + +const { Schema, model } = mongoose + +const ThoughtSchema = new Schema({ + message: { + type: String, + required: true, + minlength: 5, + maxlength: 140 + }, + hearts: { + type: Number, + default: 0 + }, + createdAt: { + type: Date, + default: () => new Date() + } +}) + +export default model('Thought', ThoughtSchema) \ No newline at end of file diff --git a/package.json b/package.json index 288b3cb..4b26fe6 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "cors": "^2.8.5", "dotenv": "^16.5.0", "express": "^4.17.3", - "express-list-endpoints": "^7.1.1" + "express-list-endpoints": "^7.1.1", + "mongoose": "^8.15.1" }, "devDependencies": { "@babel/core": "^7.27.4", diff --git a/server.js b/server.js index b103932..744fb44 100644 --- a/server.js +++ b/server.js @@ -2,15 +2,24 @@ import express from 'express' import cors from 'cors' import listEndpoints from 'express-list-endpoints' import dotenv from 'dotenv' - -import thoughts from './data/thoughts.json' +import mongoose from 'mongoose' +import Thought from './models/Thought.js' dotenv.config() const app = express() const PORT = process.env.PORT || 8080 -// Enable CORS and JSON body parsing +// Connect to MongoDB +mongoose + .connect(process.env.MONGO_URL, { + useNewUrlParser: true, + useUnifiedTopology: true + }) + .then(() => console.log('✅ Connected to MongoDB')) + .catch((err) => console.error('❌ MongoDB connection error:', err)) + +// Middleware app.use(cors()) app.use(express.json()) @@ -22,31 +31,36 @@ app.get('/', (req, res) => { }) }) -// 2) Collection endpoint: get all thoughts with optional filter by hearts -app.get('/thoughts', (req, res) => { - const { hearts } = req.query - let results = thoughts - - if (hearts) { // Filter by hearts if query parameter is provided - const h = Number(hearts) // Convert to number - results = results.filter((t) => t.hearts === h) // Filter thoughts by hearts count +// 2) Collection endpoint: get all thoughts, optional filter by hearts +app.get('/thoughts', async (req, res, next) => { + try { + const { hearts } = req.query + const filter = hearts ? { hearts: Number(hearts) } : {} + const thoughts = await Thought.find(filter).sort({ createdAt: -1 }) + res.status(200).json(thoughts) + } catch (err) { + next(err) } - - res.status(200).json(results) // Return the filtered or unfiltered thoughts }) -// 3) Single-item endpoint: get one thought by ID -app.get('/thoughts/:id', (req, res) => { - const { id } = req.params - const found = thoughts.find((t) => t._id === id) - - if (!found) { - return res - .status(404) - .json({ error: `Thought with ID '${id}' not found` }) +// 3) Single‐item endpoint: get one thought by ID +app.get('/thoughts/:id', async (req, res, next) => { + try { + const thought = await Thought.findById(req.params.id) + if (!thought) { + return res + .status(404) + .json({ error: `Thought with ID '${req.params.id}' not found` }) + } + res.status(200).json(thought) + } catch (err) { + next(err) } +}) - res.status(200).json(found) +// Error handler +app.use((err, req, res, next) => { + res.status(400).json({ error: err.message }) }) // Start the server From a43bc0c54522d7377217b98d8c395dde2b5980bb Mon Sep 17 00:00:00 2001 From: Talo Vargas Date: Mon, 9 Jun 2025 20:24:52 +0200 Subject: [PATCH 8/8] Tested and verified PUT & DELETE endpoints --- data/seed.js | 25 ++++++++++++++++++++++ data/thoughts.json | 7 ------- package.json | 3 ++- server.js | 52 ++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 data/seed.js diff --git a/data/seed.js b/data/seed.js new file mode 100644 index 0000000..cf5be0f --- /dev/null +++ b/data/seed.js @@ -0,0 +1,25 @@ +import mongoose from 'mongoose' +import dotenv from 'dotenv' +import Thought from '../models/Thought.js' +import thoughts from './thoughts.json' + +dotenv.config() + +const mongoUrl = process.env.MONGO_URL || 'mongodb://127.0.0.1:27017/happythoughts' + +mongoose + .connect(mongoUrl) + .then(async () => { + console.log('✅ Connected to MongoDB for seeding') + // 1. Clear existing documents + await Thought.deleteMany() + // 2. Insert our dummy data + const inserted = await Thought.insertMany(thoughts) + console.log(`🌱 Seeded ${inserted.length} thoughts`) + }) + .catch((err) => { + console.error('❌ Seeding error:', err) + }) + .finally(() => { + mongoose.connection.close() + }) \ No newline at end of file diff --git a/data/thoughts.json b/data/thoughts.json index a2c844f..1124d7f 100644 --- a/data/thoughts.json +++ b/data/thoughts.json @@ -110,12 +110,5 @@ "hearts": 3, "createdAt": "2025-05-20T03:57:40.322Z", "__v": 0 - }, - { - "_id": "682bab8c12155b00101732ce", - "message": "Berlin baby", - "hearts": 37, - "createdAt": "2025-05-19T22:07:08.999Z", - "__v": 0 } ] \ No newline at end of file diff --git a/package.json b/package.json index 4b26fe6..11eb5ee 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "Project API", "scripts": { "start": "babel-node server.js", - "dev": "nodemon server.js --exec babel-node" + "dev": "nodemon server.js --exec babel-node", + "seed": "babel-node data/seed.js" }, "author": "", "license": "ISC", diff --git a/server.js b/server.js index 744fb44..0f35879 100644 --- a/server.js +++ b/server.js @@ -11,11 +11,9 @@ const app = express() const PORT = process.env.PORT || 8080 // Connect to MongoDB +const mongoUrl = process.env.MONGO_URL || 'mongodb://127.0.0.1:27017/happythoughts' mongoose - .connect(process.env.MONGO_URL, { - useNewUrlParser: true, - useUnifiedTopology: true - }) + .connect(mongoUrl) .then(() => console.log('✅ Connected to MongoDB')) .catch((err) => console.error('❌ MongoDB connection error:', err)) @@ -58,6 +56,52 @@ app.get('/thoughts/:id', async (req, res, next) => { } }) +// 4) Create a new thought +app.post('/thoughts', async (req, res, next) => { + try { + const { message } = req.body + const newThought = new Thought({ message }) + const savedThought = await newThought.save() + res.status(201).json(savedThought) + } catch (err) { + next(err) + } +}) + +// 5) Update a thought (edit message or hearts) +app.put('/thoughts/:id', async (req, res, next) => { + try { + const updated = await Thought.findByIdAndUpdate( + req.params.id, + req.body, + { new: true, runValidators: true } + ) + if (!updated) { + return res + .status(404) + .json({ error: `Thought with ID '${req.params.id}' not found` }) + } + res.json(updated) + } catch (err) { + next(err) + } +}) + +// 6) Delete a thought +app.delete('/thoughts/:id', async (req, res, next) => { + try { + const deleted = await Thought.findByIdAndDelete(req.params.id) + if (!deleted) { + return res + .status(404) + .json({ error: `Thought with ID '${req.params.id}' not found` }) + } + res.json({ success: true, deletedId: req.params.id }) + } catch (err) { + next(err) + } +}) + // Error handler app.use((err, req, res, next) => { res.status(400).json({ error: err.message })