From 7b23112e8bdfdeab5bea0f52a73e5205e246a79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danilo=20Brand=C3=A3o?= Date: Mon, 13 Jul 2026 21:43:47 +0100 Subject: [PATCH 1/3] feat: setup express server, basic API routes, and CORS middleware --- server/app.js | 12 +++++++++++- server/package-lock.json | 27 +++++++++++++++++++++++++++ server/package.json | 1 + 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/server/app.js b/server/app.js index dfe51f49..0055189c 100644 --- a/server/app.js +++ b/server/app.js @@ -1,12 +1,14 @@ const express = require("express"); const morgan = require("morgan"); const cookieParser = require("cookie-parser"); +const cors = require("cors"); const PORT = 5005; // STATIC DATA // Devs Team - Import the provided files with JSON data of students and cohorts here: // ... - +const cohortsData = require("./cohorts.json"); +const studentsData = require("./students.json"); // INITIALIZE EXPRESS APP - https://expressjs.com/en/4x/api.html#express const app = express(); @@ -20,6 +22,7 @@ app.use(morgan("dev")); app.use(express.static("public")); app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); +app.use(cors()); // ROUTES - https://expressjs.com/en/starter/basic-routing.html @@ -29,6 +32,13 @@ app.get("/docs", (req, res) => { res.sendFile(__dirname + "/views/docs.html"); }); +app.get("/api/cohorts", (req, res) => { + res.json(cohortsData); +}); + +app.get("/api/students", (req, res) => { + res.json(studentsData); +}); // START SERVER app.listen(PORT, () => { diff --git a/server/package-lock.json b/server/package-lock.json index 23e2f78a..2f3fd24a 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "cookie-parser": "^1.4.6", + "cors": "^2.8.6", "express": "^4.18.2", "morgan": "^1.10.0", "nodemon": "^3.0.1" @@ -216,6 +217,23 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -700,6 +718,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", diff --git a/server/package.json b/server/package.json index 7aa0847c..cd696f7f 100644 --- a/server/package.json +++ b/server/package.json @@ -19,6 +19,7 @@ "homepage": "https://github.com/ironhack-labs/cohort-tools-project#readme", "dependencies": { "cookie-parser": "^1.4.6", + "cors": "^2.8.6", "express": "^4.18.2", "morgan": "^1.10.0", "nodemon": "^3.0.1" From 3ac6d4af76f3b0320f3547baabe43fe115aa0612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danilo=20Brand=C3=A3o?= Date: Thu, 16 Jul 2026 17:43:26 +0100 Subject: [PATCH 2/3] feat: implement Mongoose and connect to MongoDB --- server/app.js | 32 ++++++++++++++++++++++++++++++++ server/models/Cohort.model.js | 18 ++++++++++++++++++ server/models/Student.model.js | 19 +++++++++++++++++++ server/package.json | 5 +++-- 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 server/models/Cohort.model.js create mode 100644 server/models/Student.model.js diff --git a/server/app.js b/server/app.js index 0055189c..14a28d69 100644 --- a/server/app.js +++ b/server/app.js @@ -2,6 +2,7 @@ const express = require("express"); const morgan = require("morgan"); const cookieParser = require("cookie-parser"); const cors = require("cors"); +const mongoose = require("mongoose"); const PORT = 5005; // STATIC DATA @@ -9,6 +10,8 @@ const PORT = 5005; // ... const cohortsData = require("./cohorts.json"); const studentsData = require("./students.json"); +const Cohort = require("./models/Cohort.model"); +const Student = require("./models/Student.model"); // INITIALIZE EXPRESS APP - https://expressjs.com/en/4x/api.html#express const app = express(); @@ -24,6 +27,12 @@ app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); app.use(cors()); +// MONGOOSE CONNECTION +mongoose + .connect("mongodb://127.0.0.1:27017/cohort-tools-api") + .then((x) => console.log(`Connected to Mongo! Database name: "${x.connections[0].name}"`)) + .catch((err) => console.error("Error connecting to MongoDB", err)); + // ROUTES - https://expressjs.com/en/starter/basic-routing.html // Devs Team - Start working on the routes here: @@ -40,6 +49,29 @@ app.get("/api/students", (req, res) => { res.json(studentsData); }); +// GET /api/cohorts - Now pulls from MongoDB! +app.get("/api/cohorts", async (req, res) => { + try { + const cohorts = await Cohort.find(); + res.status(200).json(cohorts); + } catch (error) { + console.error("Error fetching cohorts:", error); + res.status(500).json({ message: "Failed to retrieve cohorts" }); + } +}); + + +// GET /api/students - Now pulls from MongoDB! +app.get("/api/students", async (req, res) => { + try { + const students = await Student.find().populate("cohort"); + res.status(200).json(students); + } catch (error) { + console.error("Error fetching students:", error); + res.status(500).json({ message: "Failed to retrieve students" }); + } +}); + // START SERVER app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); diff --git a/server/models/Cohort.model.js b/server/models/Cohort.model.js new file mode 100644 index 00000000..7d34e019 --- /dev/null +++ b/server/models/Cohort.model.js @@ -0,0 +1,18 @@ +const mongoose = require("mongoose"); +const Schema = mongoose.Schema; + +const cohortSchema = new Schema({ + cohortSlug: { type: String, required: true, unique: true }, + cohortName: { type: String, required: true }, + program: { type: String, enum: ["Web Dev", "UX/UI", "Data Analytics", "Cybersecurity"] }, + format: { type: String, enum: ["Full Time", "Part Time"] }, + campus: { type: String }, + startDate: { type: Date, default: Date.now }, + endDate: { type: Date }, + inProgress: { type: Boolean, default: false }, + programManager: { type: String, required: true }, + leadTeacher: { type: String, required: true }, + totalHours: { type: Number, default: 360 } +}); + +module.exports = mongoose.model("Cohort", cohortSchema); \ No newline at end of file diff --git a/server/models/Student.model.js b/server/models/Student.model.js new file mode 100644 index 00000000..c856dc66 --- /dev/null +++ b/server/models/Student.model.js @@ -0,0 +1,19 @@ +const mongoose = require("mongoose"); +const Schema = mongoose.Schema; + +const studentSchema = new Schema({ + firstName: { type: String, required: true }, + lastName: { type: String, required: true }, + email: { type: String, required: true, unique: true }, + phone: { type: String, required: true }, + linkedinUrl: { type: String, default: "" }, + languages: { type: [String], enum: ["English", "Spanish", "French", "German", "Portuguese", "Dutch", "Other"] }, + program: { type: String, enum: ["Web Dev", "UX/UI", "Data Analytics", "Cybersecurity"] }, + background: { type: String, default: "" }, + image: { type: String, default: "https://i.imgur.com/r8bo8u7.png" }, + // This establishes the relationship between a student and a cohort + cohort: { type: Schema.Types.ObjectId, ref: "Cohort" }, + projects: { type: Array } +}); + +module.exports = mongoose.model("Student", studentSchema); \ No newline at end of file diff --git a/server/package.json b/server/package.json index cd696f7f..a00539d1 100644 --- a/server/package.json +++ b/server/package.json @@ -4,8 +4,9 @@ "description": "ExpressJS API for the Cohort Tools app", "main": "app.js", "scripts": { - "dev": "nodemon app.js", - "test": "echo \"Error: no test specified\" && exit 1" + "start": "node app.js", + "dev": "nodemon app.js", + "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", From 63100842003a027351913024a38610b9bc802ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danilo=20Brand=C3=A3o?= Date: Fri, 17 Jul 2026 16:43:21 +0100 Subject: [PATCH 3/3] Day 3 implement full CRUD routes for cohorts and students --- server/app.js | 152 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 131 insertions(+), 21 deletions(-) diff --git a/server/app.js b/server/app.js index 14a28d69..72b447c2 100644 --- a/server/app.js +++ b/server/app.js @@ -5,21 +5,14 @@ const cors = require("cors"); const mongoose = require("mongoose"); const PORT = 5005; -// STATIC DATA -// Devs Team - Import the provided files with JSON data of students and cohorts here: -// ... -const cohortsData = require("./cohorts.json"); -const studentsData = require("./students.json"); +// MODELS const Cohort = require("./models/Cohort.model"); const Student = require("./models/Student.model"); -// INITIALIZE EXPRESS APP - https://expressjs.com/en/4x/api.html#express +// INITIALIZE EXPRESS APP const app = express(); - // MIDDLEWARE -// Research Team - Set up CORS middleware here: -// ... app.use(express.json()); app.use(morgan("dev")); app.use(express.static("public")); @@ -34,22 +27,17 @@ mongoose .catch((err) => console.error("Error connecting to MongoDB", err)); -// ROUTES - https://expressjs.com/en/starter/basic-routing.html -// Devs Team - Start working on the routes here: -// ... +// ========================================================= +// ROUTES +// ========================================================= + app.get("/docs", (req, res) => { res.sendFile(__dirname + "/views/docs.html"); }); -app.get("/api/cohorts", (req, res) => { - res.json(cohortsData); -}); +// --- COHORT ROUTES --- -app.get("/api/students", (req, res) => { - res.json(studentsData); -}); - -// GET /api/cohorts - Now pulls from MongoDB! +// GET /api/cohorts - Retrieve all cohorts app.get("/api/cohorts", async (req, res) => { try { const cohorts = await Cohort.find(); @@ -60,8 +48,63 @@ app.get("/api/cohorts", async (req, res) => { } }); +// POST /api/cohorts - Create a new cohort +app.post("/api/cohorts", async (req, res) => { + try { + const newCohort = await Cohort.create(req.body); + res.status(201).json(newCohort); + } catch (error) { + console.error("Error creating cohort:", error); + res.status(500).json({ message: "Failed to create cohort" }); + } +}); + +// GET /api/cohorts/:cohortId - Retrieve a specific cohort by id +app.get("/api/cohorts/:cohortId", async (req, res) => { + try { + const cohort = await Cohort.findById(req.params.cohortId); + if (!cohort) { + return res.status(404).json({ message: "Cohort not found" }); + } + res.status(200).json(cohort); + } catch (error) { + console.error("Error fetching cohort:", error); + res.status(500).json({ message: "Failed to retrieve cohort" }); + } +}); -// GET /api/students - Now pulls from MongoDB! +// PUT /api/cohorts/:cohortId - Update a specific cohort by id +app.put("/api/cohorts/:cohortId", async (req, res) => { + try { + const updatedCohort = await Cohort.findByIdAndUpdate(req.params.cohortId, req.body, { new: true }); + if (!updatedCohort) { + return res.status(404).json({ message: "Cohort not found" }); + } + res.status(200).json(updatedCohort); + } catch (error) { + console.error("Error updating cohort:", error); + res.status(500).json({ message: "Failed to update cohort" }); + } +}); + +// DELETE /api/cohorts/:cohortId - Delete a specific cohort by id +app.delete("/api/cohorts/:cohortId", async (req, res) => { + try { + const deletedCohort = await Cohort.findByIdAndDelete(req.params.cohortId); + if (!deletedCohort) { + return res.status(404).json({ message: "Cohort not found" }); + } + res.status(204).send(); + } catch (error) { + console.error("Error deleting cohort:", error); + res.status(500).json({ message: "Failed to delete cohort" }); + } +}); + + +// --- STUDENT ROUTES --- + +// GET /api/students - Retrieve all students app.get("/api/students", async (req, res) => { try { const students = await Student.find().populate("cohort"); @@ -72,7 +115,74 @@ app.get("/api/students", async (req, res) => { } }); +// POST /api/students - Create a new student +app.post("/api/students", async (req, res) => { + try { + const newStudent = await Student.create(req.body); + res.status(201).json(newStudent); + } catch (error) { + console.error("Error creating student:", error); + res.status(500).json({ message: "Failed to create student" }); + } +}); + +// GET /api/students/cohort/:cohortId - Retrieve all students for a given cohort +app.get("/api/students/cohort/:cohortId", async (req, res) => { + try { + const students = await Student.find({ cohort: req.params.cohortId }).populate("cohort"); + res.status(200).json(students); + } catch (error) { + console.error("Error fetching students by cohort:", error); + res.status(500).json({ message: "Failed to retrieve students for this cohort" }); + } +}); + +// GET /api/students/:studentId - Retrieve a specific student by id +app.get("/api/students/:studentId", async (req, res) => { + try { + const student = await Student.findById(req.params.studentId).populate("cohort"); + if (!student) { + return res.status(404).json({ message: "Student not found" }); + } + res.status(200).json(student); + } catch (error) { + console.error("Error fetching student:", error); + res.status(500).json({ message: "Failed to retrieve student" }); + } +}); + +// PUT /api/students/:studentId - Update a specific student by id +app.put("/api/students/:studentId", async (req, res) => { + try { + const updatedStudent = await Student.findByIdAndUpdate(req.params.studentId, req.body, { new: true }); + if (!updatedStudent) { + return res.status(404).json({ message: "Student not found" }); + } + res.status(200).json(updatedStudent); + } catch (error) { + console.error("Error updating student:", error); + res.status(500).json({ message: "Failed to update student" }); + } +}); + +// DELETE /api/students/:studentId - Delete a specific student by id +app.delete("/api/students/:studentId", async (req, res) => { + try { + const deletedStudent = await Student.findByIdAndDelete(req.params.studentId); + if (!deletedStudent) { + return res.status(404).json({ message: "Student not found" }); + } + res.status(204).send(); + } catch (error) { + console.error("Error deleting student:", error); + res.status(500).json({ message: "Failed to delete student" }); + } +}); + + +// ========================================================= // START SERVER +// ========================================================= app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); }); \ No newline at end of file