From 40afda6da02d36337fe5aceca2652966fa3abc39 Mon Sep 17 00:00:00 2001 From: Gary McPherson Date: Sun, 26 Mar 2023 17:49:11 +0000 Subject: [PATCH 1/3] feat: replaces Mongoose dependency - Adds error handling to API - Improves instructions --- .env.example | 3 +- README.md | 20 +++++-- package.json | 2 +- server/index.js | 36 ++++++------ server/models/Profiles.js | 10 ---- server/routes/profilesRoutes.js | 98 ++++++++++++++++++++++++--------- 6 files changed, 108 insertions(+), 61 deletions(-) delete mode 100644 server/models/Profiles.js diff --git a/.env.example b/.env.example index f06b31b..8e7cb17 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ PORT=8080 -DATABASE_CONNECTION_STRING="PUT YOUR DATABASE CONNECTION STRING HERE" \ No newline at end of file +DATABASE_CONNECTION_STRING="" +MONGO_DB_NAME="example_db" # REPLACE THIS WITH A RELEVANT NAME FOR YOUR PROJECT \ No newline at end of file diff --git a/README.md b/README.md index e2d42a9..552ab6b 100644 --- a/README.md +++ b/README.md @@ -56,21 +56,27 @@ Read the `server/` [README](./server/README.md) for more details of the example We have provided you with an example environment variables file called [`.env.example`](./.env.example). Rename this file to `.env` to use it. -In here you should assign your database connection string to the `DATABASE_CONNECTION_STRING` variable. +In here, you should copy your database connection string (which you can get from the MongoDB Atlas site or the MongoDB extension in VS Code) to the `DATABASE_CONNECTION_STRING` variable. -Make sure your connection string has the correct database name you are trying to connect to and follows this format: +Make sure your connection string follows this format: ```plain -mongodb+srv://:@cluster0.7k5er.mongodb.net/ +mongodb+srv://:@.mongodb.net ``` -For the example app the database name is `example_db`. +If your connection string has a database name appended as below, remove it: + +```plain +mongodb+srv://:@.mongodb.net/ +``` + +For the example app, the database name is `example_db`. You'll also see the `PORT` for your API in this file. Do not change this `PORT` number. 🛑 **YOUR ENVIRONMENT VARIABLES SHOULD NEVER BE COMMITED AND THE `.env` FILE HAS ALREADY BEEN ADDED TO THE [`.gitignore`](./.gitignore).** 🛑 -### Populating The Database +### Populating The Example Database If you choose to populate your database with some initial data you can do so using seed data. We have provided an example of seed data in the [`data.example/`](./server/data.example) folder in a file called [`profiles.mongodb`](server/data.example/profiles.mongodb). @@ -102,3 +108,7 @@ If all's well with the above steps, you should see a list of familiar names. If ## What's Next?! Now it's time to start building your project. + +1. Choose a new database name and update the `MONGO_DB_NAME` value in your `.env` file +2. Create a new `.mongodb` file to populate the database with your data and be sure to update the `use("")` statement with the name you chose above. +3. Define your new API routes and add to server/index.js \ No newline at end of file diff --git a/package.json b/package.json index fdd78e2..903745d 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "body-parser": "^1.19.0", "dotenv": "^16.0.3", "express": "^4.17.1", - "mongoose": "^6.9.2" + "mongodb": "^5.1.0" }, "devDependencies": { "concurrently": "^7.0.0", diff --git a/server/index.js b/server/index.js index 5f02dbd..1f79f20 100644 --- a/server/index.js +++ b/server/index.js @@ -1,29 +1,27 @@ require("dotenv").config(); const express = require("express"); -const mongoose = require("mongoose"); +const MongoClient = require("mongodb").MongoClient; const bodyParser = require("body-parser"); -// IMPORT YOUR SCHEMAS HERE -require("./models/Profiles"); //This is just an example. Don't forget to delete this - +const PORT = process.env.PORT; const app = express(); -// This is where your API is making its initial connection to the database -mongoose.Promise = global.Promise; -mongoose.set("strictQuery", false); -mongoose.connect(process.env.DATABASE_CONNECTION_STRING, { - useNewUrlParser: true, -}); - app.use(bodyParser.json()); -// IMPORT YOUR API ROUTES HERE -// Below is just an example. Don't forget to delete it. -// It's importing and using everything from the profilesRoutes.js file and also passing app as a parameter for profileRoutes to use -require("./routes/profilesRoutes")(app); +// Connect to the database +MongoClient.connect(process.env.DATABASE_CONNECTION_STRING) + .then((client) => { + const db = client.db(process.env.MONGO_DB_NAME); + // IMPORT YOUR API ROUTES HERE + // Below is just an example. Don't forget to delete it. + // It's importing and using everything from the profilesRoutes.js file and also passing app as a parameter for profileRoutes to use + require("./routes/profilesRoutes")(app, db); -const PORT = process.env.PORT; -app.listen(PORT, () => { - console.log(`API running on port ${PORT}`); -}); + app.listen(PORT, () => { + console.log(`API running on port ${PORT}`); + }); + }) + .catch((err) => { + console.error("Error: ", err); + }); \ No newline at end of file diff --git a/server/models/Profiles.js b/server/models/Profiles.js deleted file mode 100644 index a7d4ebe..0000000 --- a/server/models/Profiles.js +++ /dev/null @@ -1,10 +0,0 @@ -const mongoose = require("mongoose"); -const { Schema } = mongoose; - -const profileSchema = new Schema({ - first_name: String, - last_name: String, - location: String, -}); - -mongoose.model("profiles", profileSchema); diff --git a/server/routes/profilesRoutes.js b/server/routes/profilesRoutes.js index c328181..0da2b9e 100644 --- a/server/routes/profilesRoutes.js +++ b/server/routes/profilesRoutes.js @@ -1,43 +1,91 @@ -const mongoose = require("mongoose"); -const Profile = mongoose.model("profiles"); +const { ObjectId } = require("mongodb"); -const profileRoutes = (app) => { +/** + * @param {import('express').Express} app - The Express instance + * @param {import('mongodb').Db} db - The Db instance. + */ +const profilesRoutes = (app, db) => { + /** + * Retrieves the profiles collection from Mongo db + * @returns Collection + */ + const profilesCollection = () => db.collection("profiles"); + + /** + * Middleware handler for GET requests to /api/profile path + */ app.get(`/api/profile`, async (req, res) => { - const profiles = await Profile.find(); + try { + // Waits for asynchronous `find()` operation to complete and converts results to array + const profiles = await profilesCollection().find({}).toArray(); - return res.status(200).send(profiles); + return res.status(200).send(profiles); + } catch (e) { + return res + .status(500) + .send(`Error occurred while retrieving profiles: ${e}`); + } }); + /** + * Middleware handler for POST requests to /api/profile path + */ app.post(`/api/profile`, async (req, res) => { - const profile = await Profile.create(req.body); + try { + const profile = await profilesCollection().insertOne(req.body); - return res.status(201).send({ - error: false, - profile, - }); + return res.status(201).send({ + error: false, + profile, + }); + } catch (e) { + return res + .status(500) + .send(`Error occurred while creating profile: ${e}`); + } }); + /** + * Middleware handler for PUT requests to /api/profile/:id path + */ app.put(`/api/profile/:id`, async (req, res) => { - const { id } = req.params; - - const profile = await Profile.findByIdAndUpdate(id, req.body); + try { + // Captures target id from URL + const { id } = req.params; + // Builds query matching `_id` field value matching captured id. `ObjectId()` is needed to convert string value to correct type + const query = { _id: new ObjectId(id) }; + const profile = await profilesCollection().replaceOne(query, req.body); - return res.status(202).send({ - error: false, - profile, - }); + return res.status(202).send({ + error: false, + profile, + }); + } catch (e) { + return res + .status(500) + .send(`Error occurred while updating profilee: ${e}`); + } }); + /** + * Middleware handler for DELETE requests to /api/profile/:id path + */ app.delete(`/api/profile/:id`, async (req, res) => { - const { id } = req.params; - - const profile = await Profile.findByIdAndDelete(id); + try { + const { id } = req.params; + const query = { _id: new ObjectId(id) }; + const profile = await profilesCollection().deleteOne(query); - return res.status(202).send({ - error: false, - profile, - }); + return res.status(202).send({ + error: false, + profile, + }); + } catch (e) { + return res + .status(500) + .send(`Error occurred while deleting profiles: ${e}`); + } }); }; -module.exports = profileRoutes; +module.exports = profilesRoutes; From 9b83cab40477a1c9a138e30993259b3889f2fd3a Mon Sep 17 00:00:00 2001 From: Gary McPherson Date: Sun, 26 Mar 2023 17:50:49 +0000 Subject: [PATCH 2/3] fix: updates endpoints to plural form --- client/src/services/profileService.js | 2 +- server/routes/profilesRoutes.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/client/src/services/profileService.js b/client/src/services/profileService.js index 4ceff14..812fbe9 100644 --- a/client/src/services/profileService.js +++ b/client/src/services/profileService.js @@ -3,7 +3,7 @@ import axios from "axios"; const getAllProfiles = async () => { - const response = await axios.get(`/api/profile`); + const response = await axios.get(`/api/profiles`); return response.data || []; }; diff --git a/server/routes/profilesRoutes.js b/server/routes/profilesRoutes.js index 0da2b9e..b60ed8c 100644 --- a/server/routes/profilesRoutes.js +++ b/server/routes/profilesRoutes.js @@ -12,9 +12,9 @@ const profilesRoutes = (app, db) => { const profilesCollection = () => db.collection("profiles"); /** - * Middleware handler for GET requests to /api/profile path + * Middleware handler for GET requests to /api/profiles path */ - app.get(`/api/profile`, async (req, res) => { + app.get(`/api/profiles`, async (req, res) => { try { // Waits for asynchronous `find()` operation to complete and converts results to array const profiles = await profilesCollection().find({}).toArray(); @@ -28,9 +28,9 @@ const profilesRoutes = (app, db) => { }); /** - * Middleware handler for POST requests to /api/profile path + * Middleware handler for POST requests to /api/profiles path */ - app.post(`/api/profile`, async (req, res) => { + app.post(`/api/profiles`, async (req, res) => { try { const profile = await profilesCollection().insertOne(req.body); @@ -46,9 +46,9 @@ const profilesRoutes = (app, db) => { }); /** - * Middleware handler for PUT requests to /api/profile/:id path + * Middleware handler for PUT requests to /api/profiles/:id path */ - app.put(`/api/profile/:id`, async (req, res) => { + app.put(`/api/profiles/:id`, async (req, res) => { try { // Captures target id from URL const { id } = req.params; @@ -68,9 +68,9 @@ const profilesRoutes = (app, db) => { }); /** - * Middleware handler for DELETE requests to /api/profile/:id path + * Middleware handler for DELETE requests to /api/profiles/:id path */ - app.delete(`/api/profile/:id`, async (req, res) => { + app.delete(`/api/profiles/:id`, async (req, res) => { try { const { id } = req.params; const query = { _id: new ObjectId(id) }; From c165c53d211028cf833b210cd26d3d87f1329454 Mon Sep 17 00:00:00 2001 From: Gary McPherson Date: Sun, 26 Mar 2023 21:12:14 +0000 Subject: [PATCH 3/3] feat: adds CORS support --- package.json | 1 + server/index.js | 2 ++ 2 files changed, 3 insertions(+) diff --git a/package.json b/package.json index 903745d..55d5854 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "license": "ISC", "dependencies": { "body-parser": "^1.19.0", + "cors": "^2.8.5", "dotenv": "^16.0.3", "express": "^4.17.1", "mongodb": "^5.1.0" diff --git a/server/index.js b/server/index.js index 1f79f20..548ecf2 100644 --- a/server/index.js +++ b/server/index.js @@ -3,11 +3,13 @@ require("dotenv").config(); const express = require("express"); const MongoClient = require("mongodb").MongoClient; const bodyParser = require("body-parser"); +const cors = require("cors"); const PORT = process.env.PORT; const app = express(); app.use(bodyParser.json()); +app.use(cors()); // Connect to the database MongoClient.connect(process.env.DATABASE_CONNECTION_STRING)