diff --git a/README.md b/README.md index 6a75d8e1..a5ee8e45 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # 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. +made my own api for the happy thoughts site we built in week 7 ## The problem +Started working in the [template](https://github.com/kathinka/express-api-starter), and copied my work over into this repo. -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? +Started building the API as I have done on the last tasks +wanted to add more to is as well, som made endpoint for it, but did not complete it as I rather wanted to join my team on working on the final task. So I revisit this later when I have more time! ## 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. +View it live: +- [API](https://project-happy-thoughts-api-sw6f.onrender.com/) +- [Client](https://happyhappenings.netlify.app/) \ No newline at end of file diff --git a/model/Happythoughts.js b/model/Happythoughts.js new file mode 100644 index 00000000..4b781ab9 --- /dev/null +++ b/model/Happythoughts.js @@ -0,0 +1,23 @@ +import mongoose from "mongoose"; + +const { Schema } = mongoose; +const happyThoughtSchema = new Schema({ + message: { + type: String, + required: true, + minlength: 5, + maxlength: 140, + }, + hearts: { + type: Number, + default: 0, + }, + createdAt: { + type: Date, + default: Date.now, + }, +}); + +const HappyThought = mongoose.model("HappyThought", happyThoughtSchema); + +export default HappyThought; \ No newline at end of file diff --git a/package.json b/package.json index 1c371b45..476f1071 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "project-happy-thoughts-api", "version": "1.0.0", - "description": "Starter project to get up and running with express quickly", + "description": "API backend for happy thoughts project", "scripts": { "start": "babel-node server.js", "dev": "nodemon server.js --exec babel-node" @@ -13,8 +13,11 @@ "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", "cors": "^2.8.5", - "express": "^4.17.3", - "mongoose": "^8.0.0", + "dotenv": "^16.4.5", + "express": "^4.19.2", + "express-list-endpoints": "^7.1.0", + "mongodb": "^6.6.1", + "mongoose": "^8.3.4", "nodemon": "^3.0.1" } -} \ No newline at end of file +} diff --git a/pull_request_template.md b/pull_request_template.md index d92c89b5..06b4226b 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -1,7 +1,6 @@ ## Netlify link -Add your Netlify link here. -PS. Don't forget to add it in your readme as well. +[API](https://project-happy-thoughts-api-sw6f.onrender.com/) +[Site](https://happyhappenings.netlify.app/) ## Collaborators -Add your collaborators here. Write their GitHub usernames in square brackets. If there's more than one, separate them with a comma, like this: -[github-username-1, github-username-2] +solo diff --git a/routes/Routes.js b/routes/Routes.js new file mode 100644 index 00000000..4c08cd6c --- /dev/null +++ b/routes/Routes.js @@ -0,0 +1,184 @@ +import express from "express"; +import listEndpoints from "express-list-endpoints"; +import HappyThought from "../model/Happythoughts"; +const router = express.Router(); + +// get 100 last happy thoughts +router.get("/thoughts", async (req, res) => { + try { + const happyThoughts = await HappyThought.find() + .sort({ createdAt: "desc" }) + .limit(100) + .exec(); + res.json(happyThoughts); + } catch (error) { + res.status(400).json({ + success: false, + response: error, + message: "Could not get thoughts", + }); + } +}); + +//post new happy thought +router.post("/thoughts", async (req, res) => { + const { message } = req.body; + try { + const newHappyThought = await new HappyThought({ message }).save(); + res.status(201).json(newHappyThought); + } catch (error) { + console.error(error); // Log the error + res.status(400).json({ + success: false, + response: error, + message: "Could not save thought", + }); + } +}); + +//post a like to a happy thought +router.post("/thoughts/:thoughtId/like", async (req, res) => { + const { thoughtId } = req.params; + try { + const updatedThought = await HappyThought.findByIdAndUpdate( + thoughtId, + { $inc: { hearts: 1 } }, + { new: true } + ); + if (updatedThought) { + res.json(updatedThought); + } else { + res.status(404).json({ + success: false, + message: "Thought not found", + }); + } + } catch (error) { + res.status(400).json({ + success: false, + message: "Could not save like", + }); + } +}); + +//delete a happy thought +router.delete("/thoughts/:thoughtId", async (req, res) => { + const { thoughtId } = req.params; + try { + const deletedThought = await HappyThought.findByIdAndDelete(thoughtId); + if (deletedThought) { + res.json(deletedThought); + } else { + res.status(404).json({ + success: false, + message: "Thought not found", + }); + } + } catch (error) { + res.status(400).json({ + success: false, + message: "Could not delete thought", + }); + } +}); + +//filter happy thoughts by limit or skip or both +router.get("/thoughts/q", async (req, res) => { + const { limit = 10, page = 1 } = req.query; // Provide default values for limit and page + const skip = (parseInt(page) - 1) * parseInt(limit); // Correctly calculate skip + + try { + const thoughts = await HappyThought.find() + .sort({ createdAt: "desc" }) + .limit(parseInt(limit)) + .skip(skip) // Skip documents that have already been fetched + .exec(); + res.json(thoughts); + } catch (error) { + res.status(500).json({ message: error.message }); + } +}); + +router.get("/pages", async (req, res) => { + const limit = Number(req.query.limit) || 5; // default limit to if not provided + + try { + const count = await HappyThought.countDocuments(); // replace Thought with your model + const totalPages = Math.ceil(count / limit); + + res.json({ totalPages }); + } catch (err) { + res.status(500).json({ message: err.message }); + } +}); + +// get updated documentation +router.get("/", (req, res) => { + try { + const endpoints = listEndpoints(router); + const updatedEndpoints = endpoints.map((endpoint) => { + switch (endpoint.path) { + case "/thoughts": + return { + path: endpoint.path, + methods: endpoint.methods, + description: + "Get the last 100 happy thoughts or post a new happy thought.", + }; + case "/thoughts/q": + return { + path: endpoint.path, + methods: endpoint.methods, + description: "Filter happy thoughts by limit or skip or both.", + queryParameters: [ + { + name: "limit", + description: + "Limit the number of thoughts returned. Default is 5. example: /thoughts/q?limit=10. you can also use /thoughts/q?limit=5&page=2 to get the second page of 5 thoughts.", + }, + { + name: "page", + description: + "Paginate through thoughts based on limit. Default is 1. example: /thoughts/q?limit=5&page=2. ", + }, + ], + }; + case "/thoughts/:thoughtId/like": + return { + path: endpoint.path, + methods: endpoint.methods, + description: "Post a like to a happy thought.", + }; + case "/thoughts/:thoughtId": + return { + path: endpoint.path, + methods: ["DELETE"], + description: "Delete a happy thought.", + }; + case "/pages": + return { + path: endpoint.path, + methods: endpoint.methods, + description: + "Get the total number of pages of happy thoughts. Example: /pages.", + }; + default: + return { + path: endpoint.path, + methods: endpoint.methods, + }; + } + }); + res.json(updatedEndpoints); + } catch (error) { + const customError = new Error( + "An error occurred while fetching the endpoints" + ); + res.status(404).json({ + success: false, + message: customError.message, + }); + } +}); + +export default router; diff --git a/server.js b/server.js index dfe86fb8..ee5cb9d1 100644 --- a/server.js +++ b/server.js @@ -1,25 +1,30 @@ -import cors from "cors"; import express from "express"; +import cors from "cors"; import mongoose from "mongoose"; +import router from "./routes/Routes.js"; +import dotenv from "dotenv"; +dotenv.config(); -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( + cors({ + origin: ["https://happyhappenings.netlify.app"], + + methods: ["GET", "POST", "PUT", "DELETE"], + allowedHeaders: ["Content-Type", "Authorization", "updatedthoughtlike"], + }) +); app.use(express.json()); +app.use("/", router); -// Start defining your routes here -app.get("/", (req, res) => { - res.send("Hello Technigo!"); -}); +// Defines the port the app will run on. +const MONGO_URL = process.env.MONGO_URL || "mongodb://localhost/happyThoughts"; +mongoose.connect(MONGO_URL); +mongoose.Promise = Promise; + +const port = process.env.PORT || 8080; // Start the server app.listen(port, () => {