From 036abce37a05bd49c258d85bf7aa31585e4e86e6 Mon Sep 17 00:00:00 2001 From: Mai Kanetaka Date: Thu, 16 May 2024 14:29:14 +0200 Subject: [PATCH 01/11] install packages --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1c371b45..9ecabdb4 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,10 @@ "@babel/core": "^7.17.9", "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", + "body-parser": "^1.20.2", "cors": "^2.8.5", - "express": "^4.17.3", - "mongoose": "^8.0.0", + "express": "^4.19.2", + "mongoose": "^8.3.5", "nodemon": "^3.0.1" } -} \ No newline at end of file +} From 9edff81b7ad7c84d6d0c089145b970e548b9171e Mon Sep 17 00:00:00 2001 From: Mai Kanetaka Date: Thu, 16 May 2024 14:32:28 +0200 Subject: [PATCH 02/11] add type: module for ESM module system --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 9ecabdb4..0744c755 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "project-happy-thoughts-api", "version": "1.0.0", "description": "Starter project to get up and running with express quickly", + "type": "module", "scripts": { "start": "babel-node server.js", "dev": "nodemon server.js --exec babel-node" From 6e8273fa457243a7d3ba22d5a5576183b37f806a Mon Sep 17 00:00:00 2001 From: Mai Kanetaka Date: Thu, 16 May 2024 14:52:37 +0200 Subject: [PATCH 03/11] add thought component to define schema and model --- modules/thoughts.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 modules/thoughts.js diff --git a/modules/thoughts.js b/modules/thoughts.js new file mode 100644 index 00000000..b7b82dad --- /dev/null +++ b/modules/thoughts.js @@ -0,0 +1,20 @@ +import mongoose from "mongoose"; + +export const ThoughtSchema = new mongoose.Schema({ + message: { + type: String, + required: true, + minlength: 5, + maxlength: 140, + }, + hearts: { + type: Number, + default: 0, + }, + createdAt: { + type: Date, + default: Date.now, + }, +}); + +export const Thought = mongoose.model("Thought", ThoughtSchema); From ea01115885abf15632de12d30b41b3d0ce3199dc Mon Sep 17 00:00:00 2001 From: Mai Kanetaka Date: Thu, 16 May 2024 17:43:35 +0200 Subject: [PATCH 04/11] server component, install express-list-endpoints --- package.json | 1 + server.js | 95 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 89 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 0744c755..27e73fbd 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "body-parser": "^1.20.2", "cors": "^2.8.5", "express": "^4.19.2", + "express-list-endpoints": "^7.1.0", "mongoose": "^8.3.5", "nodemon": "^3.0.1" } diff --git a/server.js b/server.js index dfe86fb8..730ea7ac 100644 --- a/server.js +++ b/server.js @@ -1,14 +1,22 @@ import cors from "cors"; import express from "express"; import mongoose from "mongoose"; +import expressListEndpoints from "express-list-endpoints"; +import { Thought } from "./modules/thought.js"; -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo"; -mongoose.connect(mongoUrl); +// Set up MongoDB URL +const mongoUrl = + process.env.MONGO_URL || "mongodb://localhost:27017/project-mongo"; + +// Connect to MongoDB +// useNewUrlParser: true means use a newer connecting system which makes the server to be more stable +// useUnifiedTopology: true means use a newer search engine which makes the connection to be more efficient +mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); + +// ?? what is this 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(); @@ -16,10 +24,83 @@ const app = express(); app.use(cors()); app.use(express.json()); -// Start defining your routes here +// API +const routes = [ + { + path: "/thoughts", + method: "get", + description: "Get the last 20 thoughts in descending order", + handler: async (req, res) => { + try { + const allThoughts = await Thought.find() + .sort({ createdAt: -1 }) + .limit(20); + res.json(thoughts); + } catch (error) { + console.error("Error fetching thoughts", error); + res.status(500).json({ message: "Server error" }); + } + }, + }, + { + path: "/thoughts", + method: "post", + description: "Post a new thought with a message", + handler: async (req, res) => { + const { message } = req.body; + if (message.length < 5 || message.length > 140) { + return res.status(400).json({message: "Please type between between 5 and 140 characters"}) + } try { + const newThought = new Thought({ message }) + await newThought.save() + res.status(201).json(newThought) // ?? what is this + } catch (error) { + console.error("Error posting thought:", error) + res.status(500).json({message: "Server error" }) + } + } + }, + { + path: "/thoughts/:thoughtId/like", + method: "post", + description: "Like a thought by providing the thought ID", + handler: async (req, res) => { + const { thoughtId } = req.params + try { + const likedThought = await Thought.findByIdAndUpdate(thoughtId, { $inc: { hearts: 1 } }, { new: true }) + if (likedThought) { + res.status(200).json(likedThought) + } else { + res.status(404).json({ message: "Thought not found "}) + } + } catch (error) { + console.error("Error liking thought:", error) + res.status(500).json({ message: "Server error" }) + } + } + } +]; + +routes.foreach((route) => { + app[route.method](route.path, route.handler) +}) + +// API endpoints app.get("/", (req, res) => { - res.send("Hello Technigo!"); -}); + try { + const endpoints = expressListEndpoints(app).map((endpoint) => { + const matchedRoute = routes.find(route => route.path === endpoint.path) + if (matchedRoute) { + endpoint.description = matchedRoute.description + } + return endpoint + }) + res.status(200).json(endpoints) + } catch (error) { + console.error("Error generating API documentation:", error) + res.status(500).json({ message: "Server error"}) + } +}) // Start the server app.listen(port, () => { From 77976ca7a600f24f124d2ff4c7bc488bcbde58b0 Mon Sep 17 00:00:00 2001 From: Mai Kanetaka Date: Thu, 16 May 2024 17:55:02 +0200 Subject: [PATCH 05/11] fix typo --- modules/thoughts.js => models/thought.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/thoughts.js => models/thought.js (100%) diff --git a/modules/thoughts.js b/models/thought.js similarity index 100% rename from modules/thoughts.js rename to models/thought.js From 598c823e84c82efde31cde689432fcbfed8302c0 Mon Sep 17 00:00:00 2001 From: Mai Kanetaka Date: Thu, 16 May 2024 17:55:17 +0200 Subject: [PATCH 06/11] fix bugs --- server.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server.js b/server.js index 730ea7ac..a8a6c2c8 100644 --- a/server.js +++ b/server.js @@ -2,7 +2,7 @@ import cors from "cors"; import express from "express"; import mongoose from "mongoose"; import expressListEndpoints from "express-list-endpoints"; -import { Thought } from "./modules/thought.js"; +import { Thought } from "./models/thought.js"; // Set up MongoDB URL const mongoUrl = @@ -17,7 +17,7 @@ mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); mongoose.Promise = Promise; // Defines the port the app will run on. Defaults to 8080, but can be overridden -const port = process.env.PORT || 8080; +const port = process.env.PORT || 3000; const app = express(); // Add middlewares to enable cors and json body parsing @@ -32,7 +32,7 @@ const routes = [ description: "Get the last 20 thoughts in descending order", handler: async (req, res) => { try { - const allThoughts = await Thought.find() + const thoughts = await Thought.find() .sort({ createdAt: -1 }) .limit(20); res.json(thoughts); @@ -81,7 +81,7 @@ const routes = [ } ]; -routes.foreach((route) => { +routes.forEach((route) => { app[route.method](route.path, route.handler) }) From 20464bf21c3bc4ac19f02ea0b0c2541b0a9a48d0 Mon Sep 17 00:00:00 2001 From: Mai Kanetaka Date: Thu, 16 May 2024 18:05:31 +0200 Subject: [PATCH 07/11] change server --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index a8a6c2c8..44e234b2 100644 --- a/server.js +++ b/server.js @@ -17,7 +17,7 @@ mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); mongoose.Promise = Promise; // Defines the port the app will run on. Defaults to 8080, but can be overridden -const port = process.env.PORT || 3000; +const port = process.env.PORT || 8080; const app = express(); // Add middlewares to enable cors and json body parsing From d05af50294cfaa95f084a183e81e91b107ae9c9e Mon Sep 17 00:00:00 2001 From: Mai Kanetaka Date: Thu, 16 May 2024 22:24:56 +0200 Subject: [PATCH 08/11] fix bugs --- server.js | 69 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/server.js b/server.js index 44e234b2..2c6011c4 100644 --- a/server.js +++ b/server.js @@ -6,14 +6,12 @@ import { Thought } from "./models/thought.js"; // Set up MongoDB URL const mongoUrl = - process.env.MONGO_URL || "mongodb://localhost:27017/project-mongo"; + process.env.MONGO_URL || "mongodb://localhost:27017/happy-thoughts"; // Connect to MongoDB // useNewUrlParser: true means use a newer connecting system which makes the server to be more stable // useUnifiedTopology: true means use a newer search engine which makes the connection to be more efficient mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); - -// ?? what is this mongoose.Promise = Promise; // Defines the port the app will run on. Defaults to 8080, but can be overridden @@ -32,9 +30,7 @@ const routes = [ description: "Get the last 20 thoughts in descending order", handler: async (req, res) => { try { - const thoughts = await Thought.find() - .sort({ createdAt: -1 }) - .limit(20); + const thoughts = await Thought.find().sort({ createdAt: -1 }).limit(20); res.json(thoughts); } catch (error) { console.error("Error fetching thoughts", error); @@ -49,58 +45,67 @@ const routes = [ handler: async (req, res) => { const { message } = req.body; if (message.length < 5 || message.length > 140) { - return res.status(400).json({message: "Please type between between 5 and 140 characters"}) - } try { - const newThought = new Thought({ message }) - await newThought.save() - res.status(201).json(newThought) // ?? what is this + return res + .status(400) + .json({ + message: "Please type between between 5 and 140 characters", + }); + } + try { + const newThought = new Thought({ message }); + await newThought.save(); + res.status(201).json(newThought); } catch (error) { - console.error("Error posting thought:", error) - res.status(500).json({message: "Server error" }) + console.error("Error posting thought:", error); + res.status(500).json({ message: "Server error" }); } - } + }, }, { path: "/thoughts/:thoughtId/like", method: "post", description: "Like a thought by providing the thought ID", handler: async (req, res) => { - const { thoughtId } = req.params + const { thoughtId } = req.params; try { - const likedThought = await Thought.findByIdAndUpdate(thoughtId, { $inc: { hearts: 1 } }, { new: true }) + const likedThought = await Thought.findByIdAndUpdate( + thoughtId, + { $inc: { hearts: 1 } }, + { new: true } + ); if (likedThought) { - res.status(200).json(likedThought) + res.status(200).json(likedThought); } else { - res.status(404).json({ message: "Thought not found "}) + res.status(404).json({ message: "Thought not found " }); } } catch (error) { - console.error("Error liking thought:", error) - res.status(500).json({ message: "Server error" }) + console.error("Error liking thought:", error); + res.status(500).json({ message: "Server error" }); } - } - } + }, + }, ]; routes.forEach((route) => { - app[route.method](route.path, route.handler) -}) + app[route.method](route.path, route.handler); +}); // API endpoints app.get("/", (req, res) => { try { const endpoints = expressListEndpoints(app).map((endpoint) => { - const matchedRoute = routes.find(route => route.path === endpoint.path) + const matchedRoute = routes.find((route) => route.path === endpoint.path); if (matchedRoute) { - endpoint.description = matchedRoute.description + endpoint.description = matchedRoute.description; } - return endpoint - }) - res.status(200).json(endpoints) + return endpoint; + }); + res.status(200).json(endpoints); } catch (error) { - console.error("Error generating API documentation:", error) - res.status(500).json({ message: "Server error"}) + console.error("Error generating API documentation:", error); + res.status(500).json({ message: "Server error" }); } -}) +}); // Start the server app.listen(port, () => { From 47f0dcf82dcc8cae7733d7f1a56f1c0b9fe006c5 Mon Sep 17 00:00:00 2001 From: Mai Kanetaka Date: Fri, 17 May 2024 10:45:05 +0200 Subject: [PATCH 09/11] add explaination comments --- server.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/server.js b/server.js index 2c6011c4..bafe321b 100644 --- a/server.js +++ b/server.js @@ -4,7 +4,7 @@ import mongoose from "mongoose"; import expressListEndpoints from "express-list-endpoints"; import { Thought } from "./models/thought.js"; -// Set up MongoDB URL +// Set up MongoDB URL: use address in environment variables or use the localhost const mongoUrl = process.env.MONGO_URL || "mongodb://localhost:27017/happy-thoughts"; @@ -12,17 +12,20 @@ const mongoUrl = // useNewUrlParser: true means use a newer connecting system which makes the server to be more stable // useUnifiedTopology: true means use a newer search engine which makes the connection to be more efficient mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); +// Promise: handle operations that take time to finish, like fetching data from a database. 3 states: Pending, Fulfilled, Rejected mongoose.Promise = Promise; -// Defines the port the app will run on. Defaults to 8080, but can be overridden +// Defines the port the app will run on. If there's no port number in environment variables, use port 8080. const port = process.env.PORT || 8080; const app = express(); -// Add middlewares to enable cors and json body parsing +// Middlewares +// CORS: make the app accessible from other domains (websites). app.use(cors()); +// allows the app to understand JSON in request bodies. app.use(express.json()); -// API +// API - Define the routes (endpoints) const routes = [ { path: "/thoughts", @@ -30,6 +33,8 @@ const routes = [ description: "Get the last 20 thoughts in descending order", handler: async (req, res) => { try { + // `1` indicates ascending order: values sorted from smallest to largest (e.g., oldest dates to newest). + // `-1` indicates descending order: values sorted from largest to smallest (e.g., newest dates to oldest). const thoughts = await Thought.find().sort({ createdAt: -1 }).limit(20); res.json(thoughts); } catch (error) { @@ -46,7 +51,7 @@ const routes = [ const { message } = req.body; if (message.length < 5 || message.length > 140) { return res - .status(400) + .status(400) // status(400): bad request, not fullfilling the condition .json({ message: "Please type between between 5 and 140 characters", }); @@ -54,10 +59,10 @@ const routes = [ try { const newThought = new Thought({ message }); await newThought.save(); - res.status(201).json(newThought); + res.status(201).json(newThought); // status(201): a request has successfully created } catch (error) { console.error("Error posting thought:", error); - res.status(500).json({ message: "Server error" }); + res.status(500).json({ message: "Server error" }); // status(500): Internal Server Error } }, }, @@ -86,11 +91,12 @@ const routes = [ }, ]; +// these routes are added all at once to the Express application, each with its specific HTTP method and URL path and reduces code duplication routes.forEach((route) => { app[route.method](route.path, route.handler); }); -// API endpoints +// Lists all the available endpoints in the app -> quickly understand what endpoints are available without digging through code or documentation app.get("/", (req, res) => { try { const endpoints = expressListEndpoints(app).map((endpoint) => { From a4a80054228ffbe4c91651d613d6b9e8cf8c1d45 Mon Sep 17 00:00:00 2001 From: Mai Kanetaka Date: Fri, 17 May 2024 10:49:33 +0200 Subject: [PATCH 10/11] update readme --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6a75d8e1..996662cb 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,12 @@ # 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. +In this project, I developed a backend API for "Happy Thoughts", a social media platform centered on sharing positive and encouraging messages. The goal was to replicate the functionality of an existing API used in a React frontend, allowing users to post thoughts, view a list of recent thoughts, and 'like' their favorites. ## 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 used Node.js, Express, and MongoDB to create the API. I planned the project by first designing the data model and then implementing the routes needed for the frontend. If I had more time, I would add features like thought categorization and user authentication. ## 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. +Backend: https://project-happy-thoughts-api-vya8.onrender.com +Frontend: https://project-happy-thoughts-maikanetaka.netlify.app/ \ No newline at end of file From a6b4f131838311dcb31a54cc5932c6fa3d88bbcd Mon Sep 17 00:00:00 2001 From: Mai Kanetaka Date: Fri, 17 May 2024 13:30:04 +0200 Subject: [PATCH 11/11] Remove comments --- server.js | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/server.js b/server.js index bafe321b..271876e3 100644 --- a/server.js +++ b/server.js @@ -4,28 +4,18 @@ import mongoose from "mongoose"; import expressListEndpoints from "express-list-endpoints"; import { Thought } from "./models/thought.js"; -// Set up MongoDB URL: use address in environment variables or use the localhost const mongoUrl = process.env.MONGO_URL || "mongodb://localhost:27017/happy-thoughts"; -// Connect to MongoDB -// useNewUrlParser: true means use a newer connecting system which makes the server to be more stable -// useUnifiedTopology: true means use a newer search engine which makes the connection to be more efficient mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); -// Promise: handle operations that take time to finish, like fetching data from a database. 3 states: Pending, Fulfilled, Rejected mongoose.Promise = Promise; -// Defines the port the app will run on. If there's no port number in environment variables, use port 8080. const port = process.env.PORT || 8080; const app = express(); -// Middlewares -// CORS: make the app accessible from other domains (websites). app.use(cors()); -// allows the app to understand JSON in request bodies. app.use(express.json()); -// API - Define the routes (endpoints) const routes = [ { path: "/thoughts", @@ -33,8 +23,6 @@ const routes = [ description: "Get the last 20 thoughts in descending order", handler: async (req, res) => { try { - // `1` indicates ascending order: values sorted from smallest to largest (e.g., oldest dates to newest). - // `-1` indicates descending order: values sorted from largest to smallest (e.g., newest dates to oldest). const thoughts = await Thought.find().sort({ createdAt: -1 }).limit(20); res.json(thoughts); } catch (error) { @@ -51,7 +39,7 @@ const routes = [ const { message } = req.body; if (message.length < 5 || message.length > 140) { return res - .status(400) // status(400): bad request, not fullfilling the condition + .status(400) .json({ message: "Please type between between 5 and 140 characters", }); @@ -59,10 +47,10 @@ const routes = [ try { const newThought = new Thought({ message }); await newThought.save(); - res.status(201).json(newThought); // status(201): a request has successfully created + res.status(201).json(newThought); } catch (error) { console.error("Error posting thought:", error); - res.status(500).json({ message: "Server error" }); // status(500): Internal Server Error + res.status(500).json({ message: "Server error" }); } }, }, @@ -91,12 +79,11 @@ const routes = [ }, ]; -// these routes are added all at once to the Express application, each with its specific HTTP method and URL path and reduces code duplication routes.forEach((route) => { app[route.method](route.path, route.handler); }); -// Lists all the available endpoints in the app -> quickly understand what endpoints are available without digging through code or documentation + app.get("/", (req, res) => { try { const endpoints = expressListEndpoints(app).map((endpoint) => { @@ -113,7 +100,6 @@ app.get("/", (req, res) => { } }); -// Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); });