From f0099d47536247405b0f3995664ec37f7273c067 Mon Sep 17 00:00:00 2001 From: Jonash189 Date: Mon, 16 Dec 2024 08:59:06 +0100 Subject: [PATCH 1/9] Init Netflix API project: setup, seed database, and implement endpoints - Set up Express server with MongoDB using Mongoose - Added dotenv for managing environment variables - Seeded Netflix titles from JSON file into MongoDB - Implemented endpoints for fetching all titles, filtering by query parameters (country, release year, rating), and fetching a specific title by ID - Resolved issues with JSON imports and pretty-printing responses - Addressed challenges with properly formatting and querying data - Refactored and debugged code to ensure correct functionality --- package.json | 24 +++++------ server.js | 112 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 107 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 6830a48aa..3ecd99ed4 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,20 @@ { "name": "project-mongo-api", "version": "1.0.0", - "description": "Starter project to get up and running with express quickly", + "description": "Netflix titles API", + "main": "server.js", + "type": "module", "scripts": { - "start": "babel-node server.js", - "dev": "nodemon server.js --exec babel-node" + "start": "node server.js", + "dev": "nodemon server.js" }, - "author": "", - "license": "ISC", "dependencies": { - "@babel/core": "^7.17.9", - "@babel/node": "^7.16.8", - "@babel/preset-env": "^7.16.11", "cors": "^2.8.5", - "express": "^4.17.3", - "mongoose": "^8.0.0", - "nodemon": "^3.0.1" + "express": "^4.21.2", + "express-list-endpoints": "^6.0.0", + "mongoose": "^7.8.3" + }, + "devDependencies": { + "nodemon": "^3.1.7" } -} +} \ No newline at end of file diff --git a/server.js b/server.js index 647e7b144..9efdb1099 100644 --- a/server.js +++ b/server.js @@ -1,35 +1,113 @@ import express from "express"; import cors from "cors"; import mongoose from "mongoose"; +import netflixData from "./data/netflix-titles.json" assert { type: "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"; -mongoose.connect(mongoUrl); +const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/netflix_titles"; +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 -// 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 +// Middleware app.use(cors()); app.use(express.json()); -// Start defining your routes here +// Define the model +const NetflixTitle = mongoose.model("NetflixTitle", { + show_id: Number, + title: String, + country: String, + release_year: Number, + rating: String, + description: String, +}); + +// Seed the database +if (process.env.RESET_DB) { + const seedDatabase = async () => { + await NetflixTitle.deleteMany(); // Clear existing data + await NetflixTitle.insertMany(netflixData.map((item) => ({ + show_id: item.show_id, + title: item.title, + country: item.country, + release_year: item.release_year, + rating: item.rating, + description: item.description, + }))); + console.log("Database seeded!"); + }; + seedDatabase(); +} + +// Routes +import listEndpoints from "express-list-endpoints"; + +// API Documentation Route app.get("/", (req, res) => { - res.send("Hello Technigo!"); + const documentation = { + welcome: "Welcome to the Netflix Titles API!", + description: "This API provides access to a collection of Netflix titles.", + endpoints: listEndpoints(app).map((endpoint) => ({ + path: endpoint.path, + methods: endpoint.methods, + })), + queryParameters: { + "/netflix_titles": { + sorted: "Sort titles by rating (true/false)", + country: "Filter by country (case-insensitive)", + release_year: "Filter by release year", + }, + }, + }; + res.json(documentation); +}); + +// Get all Netflix titles with optional filters and pretty-print +app.get("/netflix_titles", async (req, res) => { + const { sorted, country, release_year } = req.query; + + const query = {}; + if (country) { + query.country = { $regex: country, $options: "i" }; // Case-insensitive search + } + if (release_year) { + query.release_year = Number(release_year); + } + + try { + let titles = await NetflixTitle.find(query); + if (sorted) { + titles = titles.sort((a, b) => b.rating - a.rating); // Sort by rating + } + + // Sätt header och skicka indenterad JSON + res.setHeader("Content-Type", "application/json"); + res.send(JSON.stringify(titles, null, 2)); + } catch (error) { + res.status(500).json({ error: "Internal server error" }); + } +}); + + +// Get a single Netflix title by show_id +app.get("/netflix_titles/:id", async (req, res) => { + const { id } = req.params; + + try { + const title = await NetflixTitle.findOne({ show_id: Number(id) }); + if (title) { + res.json(title); + } else { + res.status(404).json({ error: "Title not found" }); + } + } catch (error) { + res.status(500).json({ error: "Internal server error" }); + } }); -// Start the server +// Start server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); }); From 5bdfee551f98abd9d816b3abab0edcd23622e6ae Mon Sep 17 00:00:00 2001 From: Jonash189 Date: Mon, 16 Dec 2024 09:02:23 +0100 Subject: [PATCH 2/9] Adding explanation text --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index 9efdb1099..ebfb35b23 100644 --- a/server.js +++ b/server.js @@ -82,7 +82,7 @@ app.get("/netflix_titles", async (req, res) => { titles = titles.sort((a, b) => b.rating - a.rating); // Sort by rating } - // Sätt header och skicka indenterad JSON + // Set header and send indented JSON res.setHeader("Content-Type", "application/json"); res.send(JSON.stringify(titles, null, 2)); } catch (error) { From 5cbc01171b67a121e405c12dfa6f376c2e8e7bec Mon Sep 17 00:00:00 2001 From: Jonash189 Date: Mon, 16 Dec 2024 09:26:36 +0100 Subject: [PATCH 3/9] Fix JSON import and update database seeding logic --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 3ecd99ed4..39459a7d6 100644 --- a/package.json +++ b/package.json @@ -16,5 +16,8 @@ }, "devDependencies": { "nodemon": "^3.1.7" + }, + "engines": { + "node": "20.x" } } \ No newline at end of file From af71180641aefc4d89043cfe3f22bdaa3f2b4c88 Mon Sep 17 00:00:00 2001 From: Jonash189 Date: Mon, 16 Dec 2024 10:18:48 +0100 Subject: [PATCH 4/9] bugfix --- server.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server.js b/server.js index ebfb35b23..0148f073b 100644 --- a/server.js +++ b/server.js @@ -1,7 +1,10 @@ import express from "express"; import cors from "cors"; import mongoose from "mongoose"; -import netflixData from "./data/netflix-titles.json" assert { type: "json" }; +import fs from "fs"; + +const rawData = fs.readFileSync("./data/netflix-titles.json"); +const netflixData = JSON.parse(rawData); const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/netflix_titles"; mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); @@ -14,7 +17,6 @@ const app = express(); app.use(cors()); app.use(express.json()); -// Define the model const NetflixTitle = mongoose.model("NetflixTitle", { show_id: Number, title: String, @@ -22,7 +24,7 @@ const NetflixTitle = mongoose.model("NetflixTitle", { release_year: Number, rating: String, description: String, -}); +}, "netflixtitles"); // Namnet på kollektionen i MongoDB // Seed the database if (process.env.RESET_DB) { From 6b72860252ecc1b45c883c47de8e6312838fa845 Mon Sep 17 00:00:00 2001 From: Jonash189 Date: Mon, 16 Dec 2024 10:25:27 +0100 Subject: [PATCH 5/9] troobleshooting, trying to figure out why my paths works locally but not in the URL --- server.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server.js b/server.js index 0148f073b..247681e13 100644 --- a/server.js +++ b/server.js @@ -17,6 +17,7 @@ const app = express(); app.use(cors()); app.use(express.json()); +// Define the model const NetflixTitle = mongoose.model("NetflixTitle", { show_id: Number, title: String, @@ -24,7 +25,7 @@ const NetflixTitle = mongoose.model("NetflixTitle", { release_year: Number, rating: String, description: String, -}, "netflixtitles"); // Namnet på kollektionen i MongoDB +}); // Seed the database if (process.env.RESET_DB) { From 163bf72332cb98a4bade01ead3c9d0736154bd19 Mon Sep 17 00:00:00 2001 From: Jonash189 Date: Mon, 16 Dec 2024 13:50:00 +0100 Subject: [PATCH 6/9] Updting .env --- package.json | 3 ++- server.js | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 39459a7d6..38f341025 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "cors": "^2.8.5", + "dotenv": "^16.4.7", "express": "^4.21.2", "express-list-endpoints": "^6.0.0", "mongoose": "^7.8.3" @@ -20,4 +21,4 @@ "engines": { "node": "20.x" } -} \ No newline at end of file +} diff --git a/server.js b/server.js index 247681e13..15f40912c 100644 --- a/server.js +++ b/server.js @@ -2,6 +2,9 @@ import express from "express"; import cors from "cors"; import mongoose from "mongoose"; import fs from "fs"; +import dotenv from 'dotenv'; + +dotenv.config(); const rawData = fs.readFileSync("./data/netflix-titles.json"); const netflixData = JSON.parse(rawData); @@ -27,10 +30,11 @@ const NetflixTitle = mongoose.model("NetflixTitle", { description: String, }); -// Seed the database + if (process.env.RESET_DB) { const seedDatabase = async () => { - await NetflixTitle.deleteMany(); // Clear existing data + console.log("🌱 Seeding the database..."); + await NetflixTitle.deleteMany(); await NetflixTitle.insertMany(netflixData.map((item) => ({ show_id: item.show_id, title: item.title, @@ -39,7 +43,7 @@ if (process.env.RESET_DB) { rating: item.rating, description: item.description, }))); - console.log("Database seeded!"); + console.log("✅ Database seeded!"); }; seedDatabase(); } From effdf75fbacc819474a762726775495812f1718d Mon Sep 17 00:00:00 2001 From: Jonash189 Date: Mon, 16 Dec 2024 15:38:54 +0100 Subject: [PATCH 7/9] Adding env. --- server.js | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/server.js b/server.js index 15f40912c..7e68ef748 100644 --- a/server.js +++ b/server.js @@ -2,10 +2,10 @@ import express from "express"; import cors from "cors"; import mongoose from "mongoose"; import fs from "fs"; -import dotenv from 'dotenv'; - +import dotenv from "dotenv"; dotenv.config(); + const rawData = fs.readFileSync("./data/netflix-titles.json"); const netflixData = JSON.parse(rawData); @@ -34,20 +34,27 @@ const NetflixTitle = mongoose.model("NetflixTitle", { if (process.env.RESET_DB) { const seedDatabase = async () => { console.log("🌱 Seeding the database..."); - await NetflixTitle.deleteMany(); - await NetflixTitle.insertMany(netflixData.map((item) => ({ - show_id: item.show_id, - title: item.title, - country: item.country, - release_year: item.release_year, - rating: item.rating, - description: item.description, - }))); + await NetflixTitle.deleteMany(); // Rensa collectionen innan ny data läggs till + await NetflixTitle.insertMany( + netflixData.map((item) => ({ + show_id: item.show_id, + title: item.title, + director: item.director, + cast: item.cast, + country: item.country, + date_added: item.date_added, + release_year: item.release_year, + rating: item.rating, + duration: item.duration, + listed_in: item.listed_in, + description: item.description, + type: item.type, + })) + ); console.log("✅ Database seeded!"); }; seedDatabase(); } - // Routes import listEndpoints from "express-list-endpoints"; From af1d085533b47c57bd2702261614e9509494190e Mon Sep 17 00:00:00 2001 From: Jonash189 Date: Mon, 16 Dec 2024 16:02:32 +0100 Subject: [PATCH 8/9] changing collection-name/path --- server.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/server.js b/server.js index 7e68ef748..814ff91a4 100644 --- a/server.js +++ b/server.js @@ -20,15 +20,18 @@ const app = express(); app.use(cors()); app.use(express.json()); -// Define the model -const NetflixTitle = mongoose.model("NetflixTitle", { - show_id: Number, - title: String, - country: String, - release_year: Number, - rating: String, - description: String, -}); +const NetflixTitle = mongoose.model( + "NetflixTitle", // Modellnamn + { + show_id: Number, + title: String, + country: String, + release_year: Number, + rating: String, + description: String, + }, + "netflixtitles" // Explicit collection-namn +); if (process.env.RESET_DB) { From c02851e07aae478c4e60df3bf9574f040fcf829e Mon Sep 17 00:00:00 2001 From: Jonash189 Date: Mon, 16 Dec 2024 17:07:16 +0100 Subject: [PATCH 9/9] Updating my env. --- server.js | 93 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/server.js b/server.js index 814ff91a4..647cb63bd 100644 --- a/server.js +++ b/server.js @@ -5,14 +5,28 @@ import fs from "fs"; import dotenv from "dotenv"; dotenv.config(); - -const rawData = fs.readFileSync("./data/netflix-titles.json"); -const netflixData = JSON.parse(rawData); +// Läs in JSON-data med felhantering +let netflixData = []; +try { + const rawData = fs.readFileSync("./data/netflix-titles.json"); + netflixData = JSON.parse(rawData); + console.log(`✅ Loaded ${netflixData.length} titles from JSON file.`); +} catch (error) { + console.error("❌ Error reading or parsing JSON file:", error.message); +} const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/netflix_titles"; mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); mongoose.Promise = Promise; +// Kontrollera MongoDB-anslutning +mongoose.connection.on("connected", () => { + console.log(`✅ Connected to MongoDB: ${mongoose.connection.name}`); +}); +mongoose.connection.on("error", (err) => { + console.error("❌ MongoDB connection error:", err); +}); + const port = process.env.PORT || 8080; const app = express(); @@ -20,48 +34,63 @@ const app = express(); app.use(cors()); app.use(express.json()); +// Definiera Mongoose-modell med alla fält från JSON-filen const NetflixTitle = mongoose.model( - "NetflixTitle", // Modellnamn + "NetflixTitle", { show_id: Number, title: String, + director: String, + cast: String, country: String, + date_added: String, release_year: Number, rating: String, + duration: String, + listed_in: String, description: String, + type: String, }, - "netflixtitles" // Explicit collection-namn + "netflixtitles" // Explicit namn på collection ); - +// Seedningsfunktion för att lägga in data i databasen if (process.env.RESET_DB) { const seedDatabase = async () => { console.log("🌱 Seeding the database..."); - await NetflixTitle.deleteMany(); // Rensa collectionen innan ny data läggs till - await NetflixTitle.insertMany( - netflixData.map((item) => ({ - show_id: item.show_id, - title: item.title, - director: item.director, - cast: item.cast, - country: item.country, - date_added: item.date_added, - release_year: item.release_year, - rating: item.rating, - duration: item.duration, - listed_in: item.listed_in, - description: item.description, - type: item.type, - })) - ); - console.log("✅ Database seeded!"); + + try { + const deleteResult = await NetflixTitle.deleteMany(); + console.log(`🗑️ Deleted ${deleteResult.deletedCount} existing documents.`); + + const insertedDocs = await NetflixTitle.insertMany( + netflixData.map((item) => ({ + show_id: item.show_id, + title: item.title, + director: item.director, + cast: item.cast, + country: item.country, + date_added: item.date_added, + release_year: item.release_year, + rating: item.rating, + duration: item.duration, + listed_in: item.listed_in, + description: item.description, + type: item.type, + })) + ); + console.log(`✅ Successfully seeded ${insertedDocs.length} documents!`); + } catch (error) { + console.error("❌ Error seeding database:", error.message); + } }; seedDatabase(); } + // Routes import listEndpoints from "express-list-endpoints"; -// API Documentation Route +// API Dokumentation app.get("/", (req, res) => { const documentation = { welcome: "Welcome to the Netflix Titles API!", @@ -81,13 +110,13 @@ app.get("/", (req, res) => { res.json(documentation); }); -// Get all Netflix titles with optional filters and pretty-print +// Hämta alla Netflix-titlar med filter app.get("/netflix_titles", async (req, res) => { const { sorted, country, release_year } = req.query; const query = {}; if (country) { - query.country = { $regex: country, $options: "i" }; // Case-insensitive search + query.country = { $regex: country, $options: "i" }; } if (release_year) { query.release_year = Number(release_year); @@ -96,10 +125,9 @@ app.get("/netflix_titles", async (req, res) => { try { let titles = await NetflixTitle.find(query); if (sorted) { - titles = titles.sort((a, b) => b.rating - a.rating); // Sort by rating + titles = titles.sort((a, b) => a.rating.localeCompare(b.rating)); } - // Set header and send indented JSON res.setHeader("Content-Type", "application/json"); res.send(JSON.stringify(titles, null, 2)); } catch (error) { @@ -107,8 +135,7 @@ app.get("/netflix_titles", async (req, res) => { } }); - -// Get a single Netflix title by show_id +// Hämta en specifik Netflix-titel baserat på show_id app.get("/netflix_titles/:id", async (req, res) => { const { id } = req.params; @@ -124,7 +151,7 @@ app.get("/netflix_titles/:id", async (req, res) => { } }); -// Start server +// Starta servern app.listen(port, () => { - console.log(`Server running on http://localhost:${port}`); + console.log(`🚀 Server running on http://localhost:${port}`); });