From fe4cb3f65a636cef06691eeb5acbfcf3a6d2a1e2 Mon Sep 17 00:00:00 2001 From: jacquelinekellyhunt Date: Sun, 22 Dec 2024 12:01:42 +0200 Subject: [PATCH 1/5] Happy thoughts API --- package.json | 2 + server.js | 165 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 141 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index 1c371b45..b49d4137 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.4.7", "express": "^4.17.3", + "express-list-endpoints": "^7.1.1", "mongoose": "^8.0.0", "nodemon": "^3.0.1" } diff --git a/server.js b/server.js index dfe86fb8..2b5fd608 100644 --- a/server.js +++ b/server.js @@ -1,27 +1,140 @@ -import cors from "cors"; -import express from "express"; -import mongoose from "mongoose"; - -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo"; -mongoose.connect(mongoUrl); -mongoose.Promise = Promise; - -// 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(); - -// Add middlewares to 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!"); -}); - -// Start the server +import express from 'express' +import cors from 'cors' +import mongoose from 'mongoose' +import dotenv from 'dotenv' +import listEndpoints from 'express-list-endpoints' + +dotenv.config() + +// ---------------------- +// 1. Mongoose Connection +// ---------------------- +const MONGO_URL = process.env.MONGODB_URI || 'mongodb://127.0.0.1/happy-thoughts' +mongoose.connect(MONGO_URL, { + useNewUrlParser: true, + useUnifiedTopology: true +}) + .then(() => { + console.log('Connected to MongoDB successfully!') + }) + .catch((err) => { + console.error('Failed to connect to MongoDB:', err) + }) + +// ---------------------- +// 2. Mongoose Model +// ---------------------- +const ThoughtSchema = new mongoose.Schema({ + message: { + type: String, + required: true, + minlength: 5, + maxlength: 140 + }, + hearts: { + type: Number, + default: 0 + }, + // Example array of user references who have “liked” this thought + likedUsers: [ + { + type: mongoose.Schema.Types.ObjectId, + ref: 'User' + } + ], + createdAt: { + type: Date, + default: () => new Date() + } +}) + +const Thought = mongoose.model('Thought', ThoughtSchema) + +// ---------------------- +// 3. Express App Setup +// ---------------------- +const app = express() +const port = process.env.PORT || 8080 + +// Middlewares +app.use(cors({ + origin: 'https://my-happy-thoughts-frontend.netlify.app', + methods: ['GET', 'POST'], + allowedHeaders: ['Content-Type', 'Authorization'] +})) +app.use(express.json()) + +// ---------------------- +// 4. Endpoints +// ---------------------- + +// Root endpoint - shows available endpoints in JSON +app.get('/', (req, res) => { + res.json({ + message: 'Welcome to the Happy Thoughts API!', + endpoints: listEndpoints(app) + }) +}) + +// GET /thoughts - Return up to 20 thoughts, sorted by date (descending) +app.get('/thoughts', async (req, res) => { + try { + const thoughts = await Thought.find() + .sort({ createdAt: 'desc' }) + .limit(20) + .exec() + res.status(200).json(thoughts) + } catch (err) { + console.error('Error fetching thoughts:', err) + res.status(500).json({ error: 'Could not fetch thoughts' }) + } +}) + +// POST /thoughts - Create a new thought +app.post('/thoughts', async (req, res) => { + const { message } = req.body + + try { + const newThought = new Thought({ message }) + const savedThought = await newThought.save() + res.status(201).json(savedThought) + } catch (err) { + console.error('Error creating new thought:', err) + res.status(400).json({ + error: 'Could not save thought', + details: err.errors + }) + } +}) + +// POST /thoughts/:id/like - Toggle hearts for a given thought +app.post('/thoughts/:id/like', async (req, res) => { + try { + const { id } = req.params + const thought = await Thought.findById(id) + + if (!thought) { + return res.status(404).json({ error: 'Thought not found' }) + } + + // Example toggle logic: if hearts is even, increment, otherwise decrement + if (thought.hearts % 2 === 0) { + thought.hearts += 1 + } else { + thought.hearts -= 1 + } + + const updatedThought = await thought.save() + res.status(200).json(updatedThought) + } catch (err) { + console.error('Error liking the thought:', err) + res.status(500).json({ error: 'Could not update hearts' }) + } +}) + +// ---------------------- +// 5. Start the Server +// ---------------------- app.listen(port, () => { - console.log(`Server running on http://localhost:${port}`); -}); + console.log(`Server running on http://localhost:${port}`) +}) From 8d2865a33ea23ed0f76c69c06383acb3f701f024 Mon Sep 17 00:00:00 2001 From: jacquelinekellyhunt Date: Sun, 22 Dec 2024 12:10:13 +0200 Subject: [PATCH 2/5] update package json and readme --- README.md | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6a75d8e1..ef6ccf88 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,30 @@ # Project Happy Thoughts API -Replace this readme with your own information about your project. - -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. +This project is a simple REST API for storing, retrieving, and liking “Happy Thoughts”. Inspired by Twitter, but focusing on positivity. It includes endpoints to get recent thoughts, post new ones, and “like” them. ## The problem -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +I wanted to build an API that could integrate easily with a React frontend for a “happy thoughts” feed. The main goals were: + +To practice designing a MongoDB-backed API with Mongoose models. +To handle data validation (e.g., message length limits). +To deploy the API so that it can be used in a production-like setting. +Tools & Techniques +Node.js / Express for the server. +Mongoose for modeling and validation. +MongoDB for storage (hosted on [Mongo Atlas / local / etc.]). +Heroku / Render / other for deployment. +dotenv for environment variables. +If I had more time, I would add authentication, user profiles, and more thorough error handling. I’d also consider pagination or infinite scroll to handle large numbers of thoughts. + +Endpoints +GET /thoughts +Returns the 20 most recent thoughts, sorted by creation date. +POST /thoughts +Creates a new thought given a valid message. +POST /thoughts/:id/like +Increments or toggles the like count of a specific thought by ID. ## View it live -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. +[Deployed API link](https://project-happy-thoughts-api-hc1b.onrender.com/) Click to see the available endpoints and try them out! \ No newline at end of file From 126e08fa074be50e84a0748f11c9cc77b96644a2 Mon Sep 17 00:00:00 2001 From: jacquelinekellyhunt Date: Sun, 22 Dec 2024 12:20:29 +0200 Subject: [PATCH 3/5] fix errors --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index 2b5fd608..3c8f0595 100644 --- a/server.js +++ b/server.js @@ -58,7 +58,7 @@ const port = process.env.PORT || 8080 // Middlewares app.use(cors({ - origin: 'https://my-happy-thoughts-frontend.netlify.app', + origin: 'https://happiestthoughts.netlify.app', methods: ['GET', 'POST'], allowedHeaders: ['Content-Type', 'Authorization'] })) From 2b354a4523136121700afbbb71d30361928e5478 Mon Sep 17 00:00:00 2001 From: jacquelinekellyhunt Date: Sun, 22 Dec 2024 12:38:41 +0200 Subject: [PATCH 4/5] check errors --- server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server.js b/server.js index 3c8f0595..c8287c0e 100644 --- a/server.js +++ b/server.js @@ -70,6 +70,7 @@ app.use(express.json()) // Root endpoint - shows available endpoints in JSON app.get('/', (req, res) => { + console.log('REQ.BODY =>', req.body); res.json({ message: 'Welcome to the Happy Thoughts API!', endpoints: listEndpoints(app) From 9c3ef387d544fe880ca79e877e4eb913d78610d3 Mon Sep 17 00:00:00 2001 From: jacquelinekellyhunt Date: Fri, 10 Jan 2025 16:31:05 +0200 Subject: [PATCH 5/5] Mongoose methods --- server.js | 40 ++++++++-------------------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/server.js b/server.js index c8287c0e..6f1935fe 100644 --- a/server.js +++ b/server.js @@ -10,16 +10,7 @@ dotenv.config() // 1. Mongoose Connection // ---------------------- const MONGO_URL = process.env.MONGODB_URI || 'mongodb://127.0.0.1/happy-thoughts' -mongoose.connect(MONGO_URL, { - useNewUrlParser: true, - useUnifiedTopology: true -}) - .then(() => { - console.log('Connected to MongoDB successfully!') - }) - .catch((err) => { - console.error('Failed to connect to MongoDB:', err) - }) +mongoose.connect(MONGO_URL); // ---------------------- // 2. Mongoose Model @@ -35,7 +26,6 @@ const ThoughtSchema = new mongoose.Schema({ type: Number, default: 0 }, - // Example array of user references who have “liked” this thought likedUsers: [ { type: mongoose.Schema.Types.ObjectId, @@ -56,7 +46,6 @@ const Thought = mongoose.model('Thought', ThoughtSchema) const app = express() const port = process.env.PORT || 8080 -// Middlewares app.use(cors({ origin: 'https://happiestthoughts.netlify.app', methods: ['GET', 'POST'], @@ -68,16 +57,13 @@ app.use(express.json()) // 4. Endpoints // ---------------------- -// Root endpoint - shows available endpoints in JSON app.get('/', (req, res) => { - console.log('REQ.BODY =>', req.body); res.json({ message: 'Welcome to the Happy Thoughts API!', endpoints: listEndpoints(app) }) }) -// GET /thoughts - Return up to 20 thoughts, sorted by date (descending) app.get('/thoughts', async (req, res) => { try { const thoughts = await Thought.find() @@ -86,21 +72,17 @@ app.get('/thoughts', async (req, res) => { .exec() res.status(200).json(thoughts) } catch (err) { - console.error('Error fetching thoughts:', err) res.status(500).json({ error: 'Could not fetch thoughts' }) } }) -// POST /thoughts - Create a new thought app.post('/thoughts', async (req, res) => { const { message } = req.body - try { const newThought = new Thought({ message }) const savedThought = await newThought.save() res.status(201).json(savedThought) } catch (err) { - console.error('Error creating new thought:', err) res.status(400).json({ error: 'Could not save thought', details: err.errors @@ -108,27 +90,22 @@ app.post('/thoughts', async (req, res) => { } }) -// POST /thoughts/:id/like - Toggle hearts for a given thought app.post('/thoughts/:id/like', async (req, res) => { try { const { id } = req.params - const thought = await Thought.findById(id) - if (!thought) { - return res.status(404).json({ error: 'Thought not found' }) - } + const updatedThought = await Thought.findByIdAndUpdate( + id, + { $inc: { hearts: 1 } }, + { new: true } + ) - // Example toggle logic: if hearts is even, increment, otherwise decrement - if (thought.hearts % 2 === 0) { - thought.hearts += 1 - } else { - thought.hearts -= 1 + if (!updatedThought) { + return res.status(404).json({ error: 'Thought not found' }) } - const updatedThought = await thought.save() res.status(200).json(updatedThought) } catch (err) { - console.error('Error liking the thought:', err) res.status(500).json({ error: 'Could not update hearts' }) } }) @@ -137,5 +114,4 @@ app.post('/thoughts/:id/like', async (req, res) => { // 5. Start the Server // ---------------------- app.listen(port, () => { - console.log(`Server running on http://localhost:${port}`) })