From 387cb0021e8686921d72a953f1035ae3902ef533 Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Thu, 12 Dec 2024 22:43:16 +0100 Subject: [PATCH 01/15] initial commit --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index 647e7b144..5ef2e4a4f 100644 --- a/server.js +++ b/server.js @@ -17,7 +17,7 @@ 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 port = process.env.PORT || 9000; const app = express(); // Add middlewares to enable cors and json body parsing From 5ceeebdfe9a379c0daa64d9728a041df754e0b4a Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Sat, 14 Dec 2024 22:27:02 +0100 Subject: [PATCH 02/15] added routes to find id and name, added query params to filter by house, role and yearIntroduced --- data/harry-potter-characters.json | 402 ++++++++++++++++++++++++++++++ server.js | 94 +++++-- 2 files changed, 482 insertions(+), 14 deletions(-) create mode 100644 data/harry-potter-characters.json diff --git a/data/harry-potter-characters.json b/data/harry-potter-characters.json new file mode 100644 index 000000000..98290a000 --- /dev/null +++ b/data/harry-potter-characters.json @@ -0,0 +1,402 @@ +[ + { + "id": 1, + "name": "Harry Potter", + "house": "Gryffindor", + "role": "Student", + "rating": 9.8, + "yearIntroduced": 1997 + }, + { + "id": 2, + "name": "Hermione Granger", + "house": "Gryffindor", + "role": "Student", + "rating": 9.7, + "yearIntroduced": 1997 + }, + { + "id": 3, + "name": "Ron Weasley", + "house": "Gryffindor", + "role": "Student", + "rating": 9.5, + "yearIntroduced": 1997 + }, + { + "id": 4, + "name": "Albus Dumbledore", + "house": "Gryffindor", + "role": "Headmaster", + "rating": 9.9, + "yearIntroduced": 1997 + }, + { + "id": 5, + "name": "Severus Snape", + "house": "Slytherin", + "role": "Professor", + "rating": 9.6, + "yearIntroduced": 1997 + }, + { + "id": 6, + "name": "Draco Malfoy", + "house": "Slytherin", + "role": "Student", + "rating": 8.7, + "yearIntroduced": 1997 + }, + { + "id": 7, + "name": "Minerva McGonagall", + "house": "Gryffindor", + "role": "Professor", + "rating": 9.8, + "yearIntroduced": 1997 + }, + { + "id": 8, + "name": "Rubeus Hagrid", + "house": "Gryffindor", + "role": "Keeper of Keys", + "rating": 9.4, + "yearIntroduced": 1997 + }, + { + "id": 9, + "name": "Lord Voldemort", + "house": "Slytherin", + "role": "Antagonist", + "rating": 9.2, + "yearIntroduced": 1997 + }, + { + "id": 10, + "name": "Sirius Black", + "house": "Gryffindor", + "role": "Order of the Phoenix", + "rating": 9.5, + "yearIntroduced": 1999 + }, + { + "id": 11, + "name": "Remus Lupin", + "house": "Gryffindor", + "role": "Professor", + "rating": 9.6, + "yearIntroduced": 1999 + }, + { + "id": 12, + "name": "Ginny Weasley", + "house": "Gryffindor", + "role": "Student", + "rating": 8.9, + "yearIntroduced": 1997 + }, + { + "id": 13, + "name": "Neville Longbottom", + "house": "Gryffindor", + "role": "Student", + "rating": 8.8, + "yearIntroduced": 1997 + }, + { + "id": 14, + "name": "Luna Lovegood", + "house": "Ravenclaw", + "role": "Student", + "rating": 9.3, + "yearIntroduced": 2003 + }, + { + "id": 15, + "name": "Cho Chang", + "house": "Ravenclaw", + "role": "Student", + "rating": 7.8, + "yearIntroduced": 1999 + }, + { + "id": 16, + "name": "Bellatrix Lestrange", + "house": "Slytherin", + "role": "Death Eater", + "rating": 9.0, + "yearIntroduced": 1999 + }, + { + "id": 17, + "name": "Fred Weasley", + "house": "Gryffindor", + "role": "Student", + "rating": 8.5, + "yearIntroduced": 1997 + }, + { + "id": 18, + "name": "George Weasley", + "house": "Gryffindor", + "role": "Student", + "rating": 8.5, + "yearIntroduced": 1997 + }, + { + "id": 19, + "name": "Lucius Malfoy", + "house": "Slytherin", + "role": "Death Eater", + "rating": 8.2, + "yearIntroduced": 1997 + }, + { + "id": 20, + "name": "Nymphadora Tonks", + "house": "Hufflepuff", + "role": "Auror", + "rating": 9.1, + "yearIntroduced": 1999 + }, + { + "id": 21, + "name": "Cedric Diggory", + "house": "Hufflepuff", + "role": "Student", + "rating": 8.4, + "yearIntroduced": 1999 + }, + { + "id": 22, + "name": "Molly Weasley", + "house": "Gryffindor", + "role": "Order of the Phoenix", + "rating": 9.2, + "yearIntroduced": 1997 + }, + { + "id": 23, + "name": "Arthur Weasley", + "house": "Gryffindor", + "role": "Ministry Worker", + "rating": 8.7, + "yearIntroduced": 1997 + }, + { + "id": 24, + "name": "Peter Pettigrew", + "house": "Gryffindor", + "role": "Death Eater", + "rating": 6.8, + "yearIntroduced": 1997 + }, + { + "id": 25, + "name": "Fleur Delacour", + "house": "Beauxbatons", + "role": "Triwizard Champion", + "rating": 8.3, + "yearIntroduced": 2000 + }, + { + "id": 26, + "name": "Viktor Krum", + "house": "Durmstrang", + "role": "Triwizard Champion", + "rating": 8.1, + "yearIntroduced": 2000 + }, + { + "id": 27, + "name": "Moaning Myrtle", + "house": "Ravenclaw", + "role": "Ghost", + "rating": 7.5, + "yearIntroduced": 1997 + }, + { + "id": 28, + "name": "Nearly Headless Nick", + "house": "Gryffindor", + "role": "Ghost", + "rating": 7.8, + "yearIntroduced": 1997 + }, + { + "id": 29, + "name": "Dobby", + "house": "N/A", + "role": "House Elf", + "rating": 9.7, + "yearIntroduced": 1998 + }, + { + "id": 30, + "name": "Kreacher", + "house": "N/A", + "role": "House Elf", + "rating": 8.0, + "yearIntroduced": 1997 + }, + { + "id": 31, + "name": "Horace Slughorn", + "house": "Slytherin", + "role": "Professor", + "rating": 8.5, + "yearIntroduced": 2006 + }, + { + "id": 32, + "name": "Gilderoy Lockhart", + "house": "Ravenclaw", + "role": "Professor", + "rating": 6.5, + "yearIntroduced": 1992 + }, + { + "id": 33, + "name": "Argus Filch", + "house": "N/A", + "role": "Caretaker", + "rating": 5.0, + "yearIntroduced": 1997 + }, + { + "id": 34, + "name": "Peeves", + "house": "N/A", + "role": "Poltergeist", + "rating": 7.0, + "yearIntroduced": 1997 + }, + { + "id": 35, + "name": "Barty Crouch Jr.", + "house": "Slytherin", + "role": "Death Eater", + "rating": 8.1, + "yearIntroduced": 1994 + }, + { + "id": 36, + "name": "Fenrir Greyback", + "house": "N/A", + "role": "Werewolf", + "rating": 7.2, + "yearIntroduced": 1997 + }, + { + "id": 37, + "name": "Dolores Umbridge", + "house": "Slytherin", + "role": "Professor", + "rating": 6.0, + "yearIntroduced": 1995 + }, + { + "id": 38, + "name": "Mad-Eye Moody", + "house": "Hufflepuff", + "role": "Auror", + "rating": 9.0, + "yearIntroduced": 1995 + }, + { + "id": 39, + "name": "Kingsley Shacklebolt", + "house": "Hufflepuff", + "role": "Auror", + "rating": 9.2, + "yearIntroduced": 1995 + }, + { + "id": 40, + "name": "Rita Skeeter", + "house": "Ravenclaw", + "role": "Journalist", + "rating": 7.5, + "yearIntroduced": 1992 + }, + { + "id": 41, + "name": "The Grey Lady (Helena Ravenclaw)", + "house": "Ravenclaw", + "role": "Ghost", + "rating": 8.3, + "yearIntroduced": 1997 + }, + { + "id": 42, + "name": "The Bloody Baron", + "house": "Slytherin", + "role": "Ghost", + "rating": 8.0, + "yearIntroduced": 1991 + }, + { + "id": 43, + "name": "Salazar Slytherin", + "house": "Slytherin", + "role": "Founder", + "rating": 8.7, + "yearIntroduced": 1991 + }, + { + "id": 44, + "name": "Godric Gryffindor", + "house": "Gryffindor", + "role": "Founder", + "rating": 9.5, + "yearIntroduced": 1991 + }, + { + "id": 45, + "name": "Helga Hufflepuff", + "house": "Hufflepuff", + "role": "Founder", + "rating": 8.9, + "yearIntroduced": 1991 + }, + { + "id": 46, + "name": "Rowena Ravenclaw", + "house": "Ravenclaw", + "role": "Founder", + "rating": 9.2, + "yearIntroduced": 1991 + }, + { + "id": 47, + "name": "Horace Slughorn", + "house": "Slytherin", + "role": "Professor", + "rating": 8.4, + "yearIntroduced": 1997 + }, + { + "id": 48, + "name": "Narcissa Malfoy", + "house": "Slytherin", + "role": "Mother", + "rating": 7.9, + "yearIntroduced": 1997 + }, + { + "id": 49, + "name": "Astoria Greengrass", + "house": "Slytherin", + "role": "Wife", + "rating": 7.4, + "yearIntroduced": 2007 + }, + { + "id": 50, + "name": "Bill Weasley", + "house": "Gryffindor", + "role": "Order of the Phoenix", + "rating": 8.7, + "yearIntroduced": 1994 + } +] \ No newline at end of file diff --git a/server.js b/server.js index 5ef2e4a4f..e278724b1 100644 --- a/server.js +++ b/server.js @@ -1,35 +1,101 @@ import express from "express"; import cors from "cors"; import mongoose from "mongoose"; +import harryPotterCharactersData from "./data/harry-potter-characters.json"; -// If you're using one of our datasets, uncomment the appropriate import below -// to get started! -// import avocadoSalesData from "./data/avocado-sales.json"; -// import booksData from "./data/books.json"; -// import goldenGlobesData from "./data/golden-globes.json"; -// import netflixData from "./data/netflix-titles.json"; -// import topMusicData from "./data/top-music.json"; - -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo"; +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/harrypottercharacters"; 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 HarryPotterCharacter = mongoose.model("HarryPotterCharacter", { + id: Number, + name: String, + house: String, + role: String, + rating: Number, + yearIntroduced: Number +}); + const port = process.env.PORT || 9000; const app = express(); +if (process.env.RESET_DB) { + const seedDatabase = async () => { + await HarryPotterCharacter.deleteMany({}); + harryPotterCharactersData.forEach((character) => { + const newCharacter = new HarryPotterCharacter(character); + newCharacter.save(); + }); + }; + seedDatabase(); +}; + // 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!"); + res.send("Welcome to the Harry Potter API!"); +}); + +app.get("/harryPotterCharacters", async (req, res) => { + const { house, role, yearIntroduced } = req.query; // Extracting query parameters + + // Creating a dynamic query object to be able to filter on more than one thing at the time + const query = {}; + if (house) query.house = new RegExp(house, "i"); // "i" stands for case-insensitive matching: eg. makes the regular expression ignore differences between uppercase and lowercase letters when matching strings. + if (role) query.role = new RegExp(role, "i"); + if (yearIntroduced) query.yearIntroduced = +yearIntroduced; + + try { + // Query MongoDB using the dynamic query object + const characters = await HarryPotterCharacter.find(query); + + if (characters.length === 0) { + res.status(404).json({ message: "No characters found matching the criteria." }); + } else { + res.json(characters); + } + } catch (error) { + res.status(500).json({ error: "Server error", details: error.message }); + } }); + +app.get("/harryPotterCharacters/:id", async (req, res) => { + const id = req.params.id; + + try { + const harryPotterCharacter = await HarryPotterCharacter.findOne({ id: +id }); // Using +id to reassure the id is always read as a number + if (harryPotterCharacter) { + res.status(200).json(harryPotterCharacter); + } else { + res.status(404).send("Sorry - no character found with that ID"); + } + } catch (error) { + res.status(500).json({ error: "Server error", details: error.message }); + } +}); + + +app.get("/harryPotterCharacters/name/:name", async (req, res) => { + const name = req.params.name; + + try { + const harryPotterCharacterName = await HarryPotterCharacter.findOne({ name: new RegExp(`^${name}$`, "i") }); + if (harryPotterCharacterName) { + res.status(200).json(harryPotterCharacterName); + } else { + res.status(404).send("Sorry - no character found with that name"); + } + } catch (error) { + res.status(500).json({ error: "Server error", details: error.message }); + } +}); + + // Start the server app.listen(port, () => { - console.log(`Server running on http://localhost:${port}`); + console.log(`Server running on http://localhost:${port}`); }); From f690d284f1d456c66e789554d7b268659459dc07 Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Sat, 14 Dec 2024 22:30:44 +0100 Subject: [PATCH 03/15] removed the custom id to avoid redundant code --- data/harry-potter-characters.json | 50 ------------------------------- server.js | 5 ++-- 2 files changed, 2 insertions(+), 53 deletions(-) diff --git a/data/harry-potter-characters.json b/data/harry-potter-characters.json index 98290a000..016bec36c 100644 --- a/data/harry-potter-characters.json +++ b/data/harry-potter-characters.json @@ -1,6 +1,5 @@ [ { - "id": 1, "name": "Harry Potter", "house": "Gryffindor", "role": "Student", @@ -8,7 +7,6 @@ "yearIntroduced": 1997 }, { - "id": 2, "name": "Hermione Granger", "house": "Gryffindor", "role": "Student", @@ -16,7 +14,6 @@ "yearIntroduced": 1997 }, { - "id": 3, "name": "Ron Weasley", "house": "Gryffindor", "role": "Student", @@ -24,7 +21,6 @@ "yearIntroduced": 1997 }, { - "id": 4, "name": "Albus Dumbledore", "house": "Gryffindor", "role": "Headmaster", @@ -32,7 +28,6 @@ "yearIntroduced": 1997 }, { - "id": 5, "name": "Severus Snape", "house": "Slytherin", "role": "Professor", @@ -40,7 +35,6 @@ "yearIntroduced": 1997 }, { - "id": 6, "name": "Draco Malfoy", "house": "Slytherin", "role": "Student", @@ -48,7 +42,6 @@ "yearIntroduced": 1997 }, { - "id": 7, "name": "Minerva McGonagall", "house": "Gryffindor", "role": "Professor", @@ -56,7 +49,6 @@ "yearIntroduced": 1997 }, { - "id": 8, "name": "Rubeus Hagrid", "house": "Gryffindor", "role": "Keeper of Keys", @@ -64,7 +56,6 @@ "yearIntroduced": 1997 }, { - "id": 9, "name": "Lord Voldemort", "house": "Slytherin", "role": "Antagonist", @@ -72,7 +63,6 @@ "yearIntroduced": 1997 }, { - "id": 10, "name": "Sirius Black", "house": "Gryffindor", "role": "Order of the Phoenix", @@ -80,7 +70,6 @@ "yearIntroduced": 1999 }, { - "id": 11, "name": "Remus Lupin", "house": "Gryffindor", "role": "Professor", @@ -88,7 +77,6 @@ "yearIntroduced": 1999 }, { - "id": 12, "name": "Ginny Weasley", "house": "Gryffindor", "role": "Student", @@ -96,7 +84,6 @@ "yearIntroduced": 1997 }, { - "id": 13, "name": "Neville Longbottom", "house": "Gryffindor", "role": "Student", @@ -104,7 +91,6 @@ "yearIntroduced": 1997 }, { - "id": 14, "name": "Luna Lovegood", "house": "Ravenclaw", "role": "Student", @@ -112,7 +98,6 @@ "yearIntroduced": 2003 }, { - "id": 15, "name": "Cho Chang", "house": "Ravenclaw", "role": "Student", @@ -120,7 +105,6 @@ "yearIntroduced": 1999 }, { - "id": 16, "name": "Bellatrix Lestrange", "house": "Slytherin", "role": "Death Eater", @@ -128,7 +112,6 @@ "yearIntroduced": 1999 }, { - "id": 17, "name": "Fred Weasley", "house": "Gryffindor", "role": "Student", @@ -136,7 +119,6 @@ "yearIntroduced": 1997 }, { - "id": 18, "name": "George Weasley", "house": "Gryffindor", "role": "Student", @@ -144,7 +126,6 @@ "yearIntroduced": 1997 }, { - "id": 19, "name": "Lucius Malfoy", "house": "Slytherin", "role": "Death Eater", @@ -152,7 +133,6 @@ "yearIntroduced": 1997 }, { - "id": 20, "name": "Nymphadora Tonks", "house": "Hufflepuff", "role": "Auror", @@ -160,7 +140,6 @@ "yearIntroduced": 1999 }, { - "id": 21, "name": "Cedric Diggory", "house": "Hufflepuff", "role": "Student", @@ -168,7 +147,6 @@ "yearIntroduced": 1999 }, { - "id": 22, "name": "Molly Weasley", "house": "Gryffindor", "role": "Order of the Phoenix", @@ -176,7 +154,6 @@ "yearIntroduced": 1997 }, { - "id": 23, "name": "Arthur Weasley", "house": "Gryffindor", "role": "Ministry Worker", @@ -184,7 +161,6 @@ "yearIntroduced": 1997 }, { - "id": 24, "name": "Peter Pettigrew", "house": "Gryffindor", "role": "Death Eater", @@ -192,7 +168,6 @@ "yearIntroduced": 1997 }, { - "id": 25, "name": "Fleur Delacour", "house": "Beauxbatons", "role": "Triwizard Champion", @@ -200,7 +175,6 @@ "yearIntroduced": 2000 }, { - "id": 26, "name": "Viktor Krum", "house": "Durmstrang", "role": "Triwizard Champion", @@ -208,7 +182,6 @@ "yearIntroduced": 2000 }, { - "id": 27, "name": "Moaning Myrtle", "house": "Ravenclaw", "role": "Ghost", @@ -216,7 +189,6 @@ "yearIntroduced": 1997 }, { - "id": 28, "name": "Nearly Headless Nick", "house": "Gryffindor", "role": "Ghost", @@ -224,7 +196,6 @@ "yearIntroduced": 1997 }, { - "id": 29, "name": "Dobby", "house": "N/A", "role": "House Elf", @@ -232,7 +203,6 @@ "yearIntroduced": 1998 }, { - "id": 30, "name": "Kreacher", "house": "N/A", "role": "House Elf", @@ -240,7 +210,6 @@ "yearIntroduced": 1997 }, { - "id": 31, "name": "Horace Slughorn", "house": "Slytherin", "role": "Professor", @@ -248,7 +217,6 @@ "yearIntroduced": 2006 }, { - "id": 32, "name": "Gilderoy Lockhart", "house": "Ravenclaw", "role": "Professor", @@ -256,7 +224,6 @@ "yearIntroduced": 1992 }, { - "id": 33, "name": "Argus Filch", "house": "N/A", "role": "Caretaker", @@ -264,7 +231,6 @@ "yearIntroduced": 1997 }, { - "id": 34, "name": "Peeves", "house": "N/A", "role": "Poltergeist", @@ -272,7 +238,6 @@ "yearIntroduced": 1997 }, { - "id": 35, "name": "Barty Crouch Jr.", "house": "Slytherin", "role": "Death Eater", @@ -280,7 +245,6 @@ "yearIntroduced": 1994 }, { - "id": 36, "name": "Fenrir Greyback", "house": "N/A", "role": "Werewolf", @@ -288,7 +252,6 @@ "yearIntroduced": 1997 }, { - "id": 37, "name": "Dolores Umbridge", "house": "Slytherin", "role": "Professor", @@ -296,7 +259,6 @@ "yearIntroduced": 1995 }, { - "id": 38, "name": "Mad-Eye Moody", "house": "Hufflepuff", "role": "Auror", @@ -304,7 +266,6 @@ "yearIntroduced": 1995 }, { - "id": 39, "name": "Kingsley Shacklebolt", "house": "Hufflepuff", "role": "Auror", @@ -312,7 +273,6 @@ "yearIntroduced": 1995 }, { - "id": 40, "name": "Rita Skeeter", "house": "Ravenclaw", "role": "Journalist", @@ -320,7 +280,6 @@ "yearIntroduced": 1992 }, { - "id": 41, "name": "The Grey Lady (Helena Ravenclaw)", "house": "Ravenclaw", "role": "Ghost", @@ -328,7 +287,6 @@ "yearIntroduced": 1997 }, { - "id": 42, "name": "The Bloody Baron", "house": "Slytherin", "role": "Ghost", @@ -336,7 +294,6 @@ "yearIntroduced": 1991 }, { - "id": 43, "name": "Salazar Slytherin", "house": "Slytherin", "role": "Founder", @@ -344,7 +301,6 @@ "yearIntroduced": 1991 }, { - "id": 44, "name": "Godric Gryffindor", "house": "Gryffindor", "role": "Founder", @@ -352,7 +308,6 @@ "yearIntroduced": 1991 }, { - "id": 45, "name": "Helga Hufflepuff", "house": "Hufflepuff", "role": "Founder", @@ -360,7 +315,6 @@ "yearIntroduced": 1991 }, { - "id": 46, "name": "Rowena Ravenclaw", "house": "Ravenclaw", "role": "Founder", @@ -368,7 +322,6 @@ "yearIntroduced": 1991 }, { - "id": 47, "name": "Horace Slughorn", "house": "Slytherin", "role": "Professor", @@ -376,7 +329,6 @@ "yearIntroduced": 1997 }, { - "id": 48, "name": "Narcissa Malfoy", "house": "Slytherin", "role": "Mother", @@ -384,7 +336,6 @@ "yearIntroduced": 1997 }, { - "id": 49, "name": "Astoria Greengrass", "house": "Slytherin", "role": "Wife", @@ -392,7 +343,6 @@ "yearIntroduced": 2007 }, { - "id": 50, "name": "Bill Weasley", "house": "Gryffindor", "role": "Order of the Phoenix", diff --git a/server.js b/server.js index e278724b1..c759d1537 100644 --- a/server.js +++ b/server.js @@ -8,7 +8,6 @@ mongoose.connect(mongoUrl); mongoose.Promise = Promise; const HarryPotterCharacter = mongoose.model("HarryPotterCharacter", { - id: Number, name: String, house: String, role: String, @@ -40,7 +39,7 @@ app.get("/", (req, res) => { }); app.get("/harryPotterCharacters", async (req, res) => { - const { house, role, yearIntroduced } = req.query; // Extracting query parameters + const { house, role, yearIntroduced } = req.query; // Extracting query params // Creating a dynamic query object to be able to filter on more than one thing at the time const query = {}; @@ -53,7 +52,7 @@ app.get("/harryPotterCharacters", async (req, res) => { const characters = await HarryPotterCharacter.find(query); if (characters.length === 0) { - res.status(404).json({ message: "No characters found matching the criteria." }); + res.status(404).json({ message: "Sorry - no characters found" }); } else { res.json(characters); } From 0c4bd48579790bfaa66311e60d99db31520d9e28 Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Sat, 14 Dec 2024 23:03:57 +0100 Subject: [PATCH 04/15] added objectId to find the id given by mongoDB and added new to be able to create a new instance --- server.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/server.js b/server.js index c759d1537..d8d5ae447 100644 --- a/server.js +++ b/server.js @@ -8,6 +8,7 @@ mongoose.connect(mongoUrl); mongoose.Promise = Promise; const HarryPotterCharacter = mongoose.model("HarryPotterCharacter", { + id: Number, name: String, house: String, role: String, @@ -57,23 +58,24 @@ app.get("/harryPotterCharacters", async (req, res) => { res.json(characters); } } catch (error) { - res.status(500).json({ error: "Server error", details: error.message }); + res.status(400).json({ error: "Server error" }); } }); app.get("/harryPotterCharacters/:id", async (req, res) => { - const id = req.params.id; + const { id } = req.params; try { - const harryPotterCharacter = await HarryPotterCharacter.findOne({ id: +id }); // Using +id to reassure the id is always read as a number - if (harryPotterCharacter) { - res.status(200).json(harryPotterCharacter); + // Converting the string to ObjectId using "new" + const character = await HarryPotterCharacter.findById(new mongoose.Types.ObjectId(id)); + if (character) { + res.status(200).json(character); } else { - res.status(404).send("Sorry - no character found with that ID"); + res.status(404).send("Character not found"); } } catch (error) { - res.status(500).json({ error: "Server error", details: error.message }); + res.status(400).json({ error: "Invalid ID format or server error", details: error.message }); } }); @@ -89,7 +91,7 @@ app.get("/harryPotterCharacters/name/:name", async (req, res) => { res.status(404).send("Sorry - no character found with that name"); } } catch (error) { - res.status(500).json({ error: "Server error", details: error.message }); + res.status(400).json({ error: "Server error" }); } }); From 4754f51a7845c5f5f6670c751d5fa033a8b883b7 Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Sun, 15 Dec 2024 14:58:52 +0100 Subject: [PATCH 05/15] connected to atlas and added the mongo_url to the .env file --- package.json | 1 + server.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6830a48aa..04cbb9d1e 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", "cors": "^2.8.5", + "dotenv": "^16.4.7", "express": "^4.17.3", "mongoose": "^8.0.0", "nodemon": "^3.0.1" diff --git a/server.js b/server.js index d8d5ae447..95e2b2c26 100644 --- a/server.js +++ b/server.js @@ -5,7 +5,6 @@ import harryPotterCharactersData from "./data/harry-potter-characters.json"; const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/harrypottercharacters"; mongoose.connect(mongoUrl); -mongoose.Promise = Promise; const HarryPotterCharacter = mongoose.model("HarryPotterCharacter", { id: Number, From e1a03ff25bb72f72b4d6128df1d98bdd531655b4 Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Sun, 15 Dec 2024 15:25:05 +0100 Subject: [PATCH 06/15] test push to see if the page works on render --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index 95e2b2c26..43681d929 100644 --- a/server.js +++ b/server.js @@ -78,7 +78,7 @@ app.get("/harryPotterCharacters/:id", async (req, res) => { } }); - +//finding only the name app.get("/harryPotterCharacters/name/:name", async (req, res) => { const name = req.params.name; From c6884e93df90f77ebee18bbd289e6e2d38857e8a Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Sun, 15 Dec 2024 15:42:09 +0100 Subject: [PATCH 07/15] test 2 to see if deploy works on render, added reset_db=true in the .env file --- server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server.js b/server.js index 43681d929..98a97f836 100644 --- a/server.js +++ b/server.js @@ -29,6 +29,7 @@ if (process.env.RESET_DB) { seedDatabase(); }; + // Add middlewares to enable cors and json body parsing app.use(cors()); app.use(express.json()); From 86f6a1ad7827bdae09fcf5118492ad57c262747e Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Sun, 15 Dec 2024 18:29:01 +0100 Subject: [PATCH 08/15] imported dotenv because i forgot to do so --- server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server.js b/server.js index 98a97f836..dfa7f61e4 100644 --- a/server.js +++ b/server.js @@ -1,6 +1,7 @@ import express from "express"; import cors from "cors"; import mongoose from "mongoose"; +import dotenv from "dotenv"; import harryPotterCharactersData from "./data/harry-potter-characters.json"; const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/harrypottercharacters"; From e155999ea75e17d089ff2606026de6958993866e Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Sun, 15 Dec 2024 18:40:17 +0100 Subject: [PATCH 09/15] added dotenv.config in server.js --- README.md | 4 +--- server.js | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 35019cd8a..672578363 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # Project Mongo 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 is an API built using Node.js, Express, and MongoDB, designed to provide information about Harry Potter characters. The API allows users to retrieve details about characters, filter them by various attributes, and query individual characters based on their unique IDs or names. ## The problem diff --git a/server.js b/server.js index dfa7f61e4..95df6a36a 100644 --- a/server.js +++ b/server.js @@ -4,6 +4,8 @@ import mongoose from "mongoose"; import dotenv from "dotenv"; import harryPotterCharactersData from "./data/harry-potter-characters.json"; +dotenv.config(); + const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/harrypottercharacters"; mongoose.connect(mongoUrl); From 24051de6f8adf71ccf8af3b5e829a99f6c478b1d Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Sun, 15 Dec 2024 20:39:14 +0100 Subject: [PATCH 10/15] added content to the readme --- README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 672578363..c70ccd04c 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,22 @@ This is an API built using Node.js, Express, and MongoDB, designed to provide information about Harry Potter characters. The API allows users to retrieve details about characters, filter them by various attributes, and query individual characters based on their unique IDs or names. -## The problem +## The problem & process -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 began by importing my JSON file containing Harry Potter characters and defining a Mongoose model. Next, I created an environment function to seed the database. Since I had previously created routes for a similar API using plain JavaScript, I reused those routes and endpoints but adapted them to work with Mongoose. + +- I wanted to allow users to filter characters by role, house, and year using query parameters, enabling them to filter by multiple criteria at once. To achieve this, I created a dynamic query object using conditional statements. With Mongoose's find method, the query object enabled filtering on individual attributes or a combination of them. + +- To make the filtering case-insensitive, I used regular expressions with the i flag, allowing users to enter queries in both lowercase and uppercase letters. + +- Initially, I used the id from the JSON file to filter characters by their unique identifier. However, since MongoDB automatically generates an ObjectId, I decided to remove the custom id field from the JSON file. Instead, I used new mongoose.Types.ObjectId to generate new IDs and implemented filtering based on the default ObjectId. + +## Problem with deployment +I successfully connected the API to MongoDB Atlas, and the data is now visible there. To secure the connection, the Atlas URL along with the username and password is stored in a .env file. + +- However, when deploying the API on Render, only the root route (/) works, while the other routes fail to display. This suggests that Render cannot establish a connection to the MongoDB Atlas URL provided in the .env file. Despite attempting several solutions, the issue remains unresolved. ## 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. +https://project-mongo-api-8bgk.onrender.com/ + From 32d52f64bd6bae3482d2ffdc3f0851bf63074411 Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Sun, 15 Dec 2024 22:00:58 +0100 Subject: [PATCH 11/15] added comments to remember what has been done and changed --- server.js | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/server.js b/server.js index 95df6a36a..5191f6961 100644 --- a/server.js +++ b/server.js @@ -6,9 +6,11 @@ import harryPotterCharactersData from "./data/harry-potter-characters.json"; dotenv.config(); +//using the Atlas URL from .env, or fall back to local MongoDB localhost const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/harrypottercharacters"; mongoose.connect(mongoUrl); +// Defining a Mongoose model for a Harry Potter character const HarryPotterCharacter = mongoose.model("HarryPotterCharacter", { id: Number, name: String, @@ -21,11 +23,12 @@ const HarryPotterCharacter = mongoose.model("HarryPotterCharacter", { const port = process.env.PORT || 9000; const app = express(); +// If the RESET_DB environment variable is true, the database is seeded with JSON data if (process.env.RESET_DB) { const seedDatabase = async () => { - await HarryPotterCharacter.deleteMany({}); + await HarryPotterCharacter.deleteMany({}); // Clears the existing database harryPotterCharactersData.forEach((character) => { - const newCharacter = new HarryPotterCharacter(character); + const newCharacter = new HarryPotterCharacter(character); // Creating a new character instance newCharacter.save(); }); }; @@ -37,6 +40,15 @@ if (process.env.RESET_DB) { app.use(cors()); app.use(express.json()); +// Middleware to handle 503 errors +app.use((req, res, next) => { + if (mongoose.connection.readyState === 1) { + next() + } else { + res.status(503).json({ error: "Service unavailable" }); + } +}); + // Start defining your routes here app.get("/", (req, res) => { res.send("Welcome to the Harry Potter API!"); @@ -46,11 +58,11 @@ app.get("/harryPotterCharacters", async (req, res) => { const { house, role, yearIntroduced } = req.query; // Extracting query params // Creating a dynamic query object to be able to filter on more than one thing at the time - const query = {}; - if (house) query.house = new RegExp(house, "i"); // "i" stands for case-insensitive matching: eg. makes the regular expression ignore differences between uppercase and lowercase letters when matching strings. + const query = {}; //Initially an empty query object before the user has applied filters + if (house) query.house = new RegExp(house, "i"); //Depending on the query parameters provided - properties are dynamically added to the query object. if (role) query.role = new RegExp(role, "i"); if (yearIntroduced) query.yearIntroduced = +yearIntroduced; - + // "i" stands for case-insensitive matching: eg. makes the regular expression ignore differences between uppercase and lowercase letters when matching strings. try { // Query MongoDB using the dynamic query object const characters = await HarryPotterCharacter.find(query); @@ -67,7 +79,7 @@ app.get("/harryPotterCharacters", async (req, res) => { app.get("/harryPotterCharacters/:id", async (req, res) => { - const { id } = req.params; + const { id } = req.params; // Extracting the id from the URL try { // Converting the string to ObjectId using "new" @@ -82,11 +94,11 @@ app.get("/harryPotterCharacters/:id", async (req, res) => { } }); -//finding only the name +//finding the unique character name app.get("/harryPotterCharacters/name/:name", async (req, res) => { - const name = req.params.name; + const name = req.params.name; // Extracting the name from the URL - try { + try { // "new RegExp(`^${name}$`, "i")" ensures case-insensitive matching const harryPotterCharacterName = await HarryPotterCharacter.findOne({ name: new RegExp(`^${name}$`, "i") }); if (harryPotterCharacterName) { res.status(200).json(harryPotterCharacterName); From 0a32eec9903cb8dd30e3500d1c52586b27a68042 Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Tue, 17 Dec 2024 22:20:58 +0100 Subject: [PATCH 12/15] imported listEndpoints and added to the first route --- package.json | 1 + server.js | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 04cbb9d1e..0e1445df8 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "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 5191f6961..30e19bb69 100644 --- a/server.js +++ b/server.js @@ -2,6 +2,7 @@ import express from "express"; import cors from "cors"; import mongoose from "mongoose"; import dotenv from "dotenv"; +import listEndpoints from "express-list-endpoints"; import harryPotterCharactersData from "./data/harry-potter-characters.json"; dotenv.config(); @@ -51,7 +52,10 @@ app.use((req, res, next) => { // Start defining your routes here app.get("/", (req, res) => { - res.send("Welcome to the Harry Potter API!"); + res.json({ + message: "Welcome to the Harry Potter API!", + routes: listEndpoints(app), + }); }); app.get("/harryPotterCharacters", async (req, res) => { From 8c71b771d08204fd6012909422a11a2cdc0a2771 Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Tue, 17 Dec 2024 22:29:23 +0100 Subject: [PATCH 13/15] removed the problems with deploying-part of the readme since that was solved using listEndpoints --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index 30e19bb69..21811058c 100644 --- a/server.js +++ b/server.js @@ -50,7 +50,7 @@ app.use((req, res, next) => { } }); -// Start defining your routes here +// Start defining your routes here (added listEndpoints) app.get("/", (req, res) => { res.json({ message: "Welcome to the Harry Potter API!", From d1a3f359d0939d154d509408f7e5c171d15dcbc2 Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Tue, 17 Dec 2024 22:31:48 +0100 Subject: [PATCH 14/15] test --- server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index 21811058c..dcdd46c55 100644 --- a/server.js +++ b/server.js @@ -63,10 +63,10 @@ app.get("/harryPotterCharacters", async (req, res) => { // Creating a dynamic query object to be able to filter on more than one thing at the time const query = {}; //Initially an empty query object before the user has applied filters - if (house) query.house = new RegExp(house, "i"); //Depending on the query parameters provided - properties are dynamically added to the query object. + if (house) query.house = new RegExp(house, "i"); //Depending on the query parameters provided - properties are dynamically added to the query object if (role) query.role = new RegExp(role, "i"); if (yearIntroduced) query.yearIntroduced = +yearIntroduced; - // "i" stands for case-insensitive matching: eg. makes the regular expression ignore differences between uppercase and lowercase letters when matching strings. + // "i" stands for case-insensitive matching: eg. makes the regular expression ignore differences between uppercase and lowercase letters try { // Query MongoDB using the dynamic query object const characters = await HarryPotterCharacter.find(query); From 1acb977eb12dea7b35ca7df5e22bb208aff69e05 Mon Sep 17 00:00:00 2001 From: iamfannyhenriques Date: Sun, 22 Dec 2024 16:24:34 +0100 Subject: [PATCH 15/15] removed old readme text --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index c70ccd04c..9f511b9ef 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,6 @@ This is an API built using Node.js, Express, and MongoDB, designed to provide in - Initially, I used the id from the JSON file to filter characters by their unique identifier. However, since MongoDB automatically generates an ObjectId, I decided to remove the custom id field from the JSON file. Instead, I used new mongoose.Types.ObjectId to generate new IDs and implemented filtering based on the default ObjectId. -## Problem with deployment -I successfully connected the API to MongoDB Atlas, and the data is now visible there. To secure the connection, the Atlas URL along with the username and password is stored in a .env file. - -- However, when deploying the API on Render, only the root route (/) works, while the other routes fail to display. This suggests that Render cannot establish a connection to the MongoDB Atlas URL provided in the .env file. Despite attempting several solutions, the issue remains unresolved. - ## View it live https://project-mongo-api-8bgk.onrender.com/