--- title: 'Lab 7: FocusTools Backend with MongoDB' tags: CS195, Full Stack, MongoDB, Express, Backend description: Build a complete backend API for FocusTools with database persistence --- Lab 7: FocusTools Backend with MongoDB ====================================== **CS195 Full Stack Web Development** **Assigned:** Wednesday, November 13, 2024 **Due:** Tuesday, November 18, 2024 by 11:59 PM **Submission:** Via Blackboard (GitHub commit links + screenshots) --- Lab Overview ------------ You've built the pieces of the frontend for **FocusTools** in Lab05 and Lab06—a task list and Pomodoro timer. Over the past two classes (Days 17, 18 and 19), you learned about Express backends and MongoDB databases. Now it's time to build the API! **In this lab, you're focusing entirely on the backend.** You'll build a complete REST API that: - Stores tasks and Pomodoro sessions in MongoDB - Persists data across sessions (data lives in the database!) - Provides CRUD endpoints that could power any frontend - Follows industry-standard REST API patterns **What makes this important:** You're learning how professional backend developers work. In real companies, backend and frontend teams often work in parallel - the backend team builds the API while the frontend team uses mock data, then they integrate later. This lab teaches you those backend skills. **Testing approach:** You'll use **Thunder Client** to test all your routes. No React, no UI - just pure backend API development. This lets you focus on understanding how data flows from Express to MongoDB and back. **Timeline:** You have **5 days** to complete this lab. Project #2 launches on Day 20 (Nov 18), and you'll do full-stack integration there! --- Learning Objectives ------------------- By the end of this lab, you'll be able to: - Define Mongoose schemas for complex data structures - Build complete REST APIs with all CRUD operations - Connect Express routes to MongoDB using Mongoose - Test API routes using Thunder Client - Handle errors and validation in backend routes - Structure a professional backend project - Verify data persistence in MongoDB Atlas --- Getting Started --------------- ### 1\. Accept the GitHub Classroom Assignment **GitHub Classroom Link:** https://classroom.github.com/a/POYnoLAq 1. Click the assignment link 2. Accept the assignment 3. GitHub will create your personal repository 4. Clone the repository to your computer: ```bash cd ~/Desktop/CS195 # or wherever you keep your projects git clone [your-repo-url] cd [repo-name] ``` ### 2\. Install Dependencies ```bash npm install ``` This installs: - `express` - Web framework - `mongoose` - MongoDB ODM - `dotenv` - Environment variable management - `cors` - Cross-origin requests (so frontend can talk to backend) ### 3\. Set Up Environment Variables Create a `.env` file in your project root: ``` MONGODB_URI=your_connection_string_from_atlas PORT=3001 ``` **⚠️ IMPORTANT:** Add `.env` to your `.gitignore` so you don't accidentally commit your database credentials! ### 4\. Verify Setup ```bash npm start ``` You should see: ``` 🚀 Server running on http://localhost:3001 ✅ Connected to MongoDB ``` **✋ If you see errors, raise your hand in class or come to office hours!** --- Project Structure ----------------- ``` focustools-backend/ ├── models/ │ ├── Task.js ← You'll create this │ └── Session.js ← You'll create this ├── server.js ← Your API routes go here ├── .env ← Your secrets (don't commit!) ├── .gitignore ← Includes .env ├── package.json ← Dependencies └── README.md ← Instructions ``` --- Part 1: Task Model & Routes ------------------------------------------ ### Task Schema Create `models/Task.js` with this structure: ```javascript const mongoose = require('mongoose'); const taskSchema = new mongoose.Schema({ title: { type: String, required: true, trim: true }, completed: { type: Boolean, default: false } }, { timestamps: true // Adds createdAt and updatedAt }); const Task = mongoose.model('Task', taskSchema); module.exports = Task; ``` **Things to notice:** - Simple schema to start (just `title` and `completed`) - `completed` defaults to `false` for new tasks - `timestamps: true` automatically tracks when tasks are created/updated - Validation rules prevent invalid data **Keep it simple:** Focus on getting these core fields working perfectly. You can always extend the schema later for advanced features! --- ### Task Routes (Build all 5) Build these routes in `server.js`: #### 1\. CREATE - Add a new task ```javascript POST /api/tasks ``` **Request body:** ```json { "title": "Write research paper", "completed": false } ``` **Response:** The created task with `_id` and timestamps **Status codes:** - `201` - Task created successfully - `400` - Validation error (missing required fields) --- #### 2. READ ALL - Get all tasks ```javascript GET /api/tasks ``` **Response:** Array of all tasks **Example:** ```json [ { "_id": "507f1f77bcf86cd799439011", "title": "Write research paper", "completed": false, "createdAt": "2024-11-04T14:30:00.000Z", "updatedAt": "2024-11-04T14:30:00.000Z" }, {...} ] ``` **Status codes:** - `200` - Success - `500` - Server error --- #### 3. READ ONE - Get a specific task ```javascript GET /api/tasks/:id ``` **URL parameter:** `id` - MongoDB ObjectId **Response:** Single task object **Status codes:** - `200` - Task found - `404` - Task not found - `500` - Server error --- #### 4. UPDATE - Modify a task ```javascript PUT /api/tasks/:id ``` **Request body:** Fields to update (can be partial) **Example - Mark task as complete:** ```json { "completed": true } ``` **Example - Update title:** ```json { "title": "Write research paper (updated deadline)" } ``` **Response:** Updated task **Status codes:** - `200` - Task updated - `404` - Task not found - `400` - Validation error --- #### 5. DELETE - Remove a task ```javascript DELETE /api/tasks/:id ``` **Response:** ```json { "message": "Task deleted successfully", "task": {...} // The deleted task } ``` **Status codes:** - `200` - Task deleted - `404` - Task not found - `500` - Server error --- ### Testing Your Task Routes Use **Thunder Client** in VS Code to test each route: #### Test Checklist: - [ ] **CREATE** - Can add a new task with title - [ ] **READ ALL** - Can get all tasks - [ ] **READ ONE** - Can get a specific task by ID - [ ] **UPDATE** - Can mark task as complete - [ ] **UPDATE** - Can update task title - [ ] **DELETE** - Can delete a task - [ ] **ERROR** - Get proper error when ID doesn't exist - [ ] **ERROR** - Get validation error when title is missing **💡 TIP:** Create a Thunder Client collection with all your requests so you can retest quickly! Part 2: Session Model & Routes (30% of grade) --------------------------------------------- ### Session Schema Create `models/Session.js`: ```javascript const mongoose = require('mongoose'); const sessionSchema = new mongoose.Schema({ taskId: { type: mongoose.Schema.Types.ObjectId, ref: 'Task', // References Task model required: true }, duration: { type: Number, // Duration in seconds required: true, min: 1 }, completed: { type: Boolean, default: true // Assume completed when logged }, startTime: { type: Date, required: true } }, { timestamps: true }); const Session = mongoose.model('Session', sessionSchema); module.exports = Session; ``` **Key features:** - `taskId` references a Task (relationship between collections!) - `duration` stored in seconds (25 min = 1500 seconds) - `startTime` tracks when Pomodoro began - `completed` tracks if full session was finished --- ### Session Routes (Build 2) #### 1. CREATE - Log a completed Pomodoro session ```javascript POST /api/sessions ``` **Request body:** ```json { "taskId": "507f1f77bcf86cd799439011", "duration": 1500, "startTime": "2024-11-07T14:00:00.000Z", "completed": true } ``` **Response:** Created session with `_id` **Status codes:** - `201` - Session logged - `400` - Validation error --- #### 2. READ ALL - Get all Pomodoro sessions ```javascript GET /api/sessions ``` **Response:** Array of all sessions **Bonus:** Use `.populate('taskId')` to include task details: ```javascript const sessions = await Session.find().populate('taskId'); ``` This changes the response from: ```json {"taskId": "507f1f77bcf86cd799439011"} ``` To: ```json {"taskId": {"_id": "...", "title": "Write research paper", ...}} ``` **Status codes:** - `200` - Success - `500` - Server error --- ### Testing Your Session Routes #### Test Checklist: - [ ] **CREATE** - Can log a Pomodoro session - [ ] **READ ALL** - Can get all sessions - [ ] **BONUS** - Sessions include task details (`.populate()`) - [ ] **ERROR** - Get validation error when taskId is invalid --- Extensions (Optional - No Extra Credit) --------------------------------------- These extensions are **optional challenges** for students who want to explore further. They do not provide extra credit but are great practice for Project #2! ### ⭐⭐ Extension 1: Query Filtering (Easy) Add support for filtering tasks by completion status: ```javascript GET /api/tasks?completed=true GET /api/tasks?completed=false ``` **Implementation hint:** ```javascript app.get('/api/tasks', async (req, res) => { const { completed } = req.query; let filter = {}; if (completed !== undefined) { filter.completed = completed === 'true'; } const tasks = await Task.find(filter); res.json(tasks); }); ``` **Test it:** Try both URLs in Thunder Client and verify filtering works! --- ### ⭐⭐⭐ Extension 2: Session Statistics (Medium) Create an endpoint that calculates productivity stats: ```javascript GET /api/stats ``` **Response:** ```json { "totalPomodoros": 42, "totalMinutes": 1050, "completedTasks": 8, "activeTasks": 5 } ``` **Implementation hints:** - Use `Session.countDocuments()` for total Pomodoros - Use `Session.aggregate()` to sum durations - Use `Task.countDocuments({completed: true})` for completed tasks - Use `Task.countDocuments({completed: false})` for active tasks --- ### ⭐⭐⭐ Extension 3: Populate Task Info in Sessions (Medium-Hard) Make GET `/api/sessions` include full task details instead of just taskId: **Before (just ID):** ```json { "_id": "...", "taskId": "507f1f77bcf86cd799439011", "duration": 1500 } ``` **After (full task):** ```json { "_id": "...", "taskId": { "_id": "507f1f77bcf86cd799439011", "title": "Write research paper", "completed": false }, "duration": 1500 } ``` **Implementation:** ```javascript const sessions = await Session.find().populate('taskId'); ``` --- ### ⭐⭐⭐⭐ Extension 4: Advanced Features (Hard) Choose one (or more!): **A) Task Search** ```javascript GET /api/tasks/search?q=research ``` Returns tasks where title contains "research" (case-insensitive) **Hint:** Use MongoDB's `$regex` operator **B) Session History for a Task** ```javascript GET /api/tasks/:id/sessions ``` Returns all Pomodoro sessions for a specific task **Hint:** Query sessions with `{ taskId: req.params.id }` **C) Sorting** ```javascript GET /api/sessions?sortBy=date&order=desc GET /api/tasks?sortBy=title&order=asc ``` Add sorting capability to your routes **Hint:** Use `.sort()` method on Mongoose queries --- ### If You Complete Extensions: Feel free to include them in your submission! While they don't add extra credit, they demonstrate advanced understanding and make excellent talking points for Project #2 or job interviews. **For your screenshots:** You can optionally add a 5th screenshot showing your extension working, but it's not required for full credit. --- Submission Requirements ----------------------- ### What to Submit on Blackboard: #### 1\. GitHub Repository Link (Required) - Must be a **clickable link** to your Lab 7 repository - Repository must be public or instructor must have access - Code must be committed and pushed before deadline #### 2\. Four Screenshots (Required) Follow the requirements in the **Grading Rubric** section above: - **Screenshot 1**: Task CRUD routes working in Thunder Client - **Screenshot 2**: Task UPDATE & DELETE routes working in Thunder Client - **Screenshot 3**: Session routes working in Thunder Client - **Screenshot 4**: MongoDB Atlas showing both collections with data ### Submission Template Copy this into the Blackboard text box: ``` GitHub Repository: [paste your link here] Screenshot 1: Task CRUD Routes (CREATE, READ all, READ one) [attach image below] Screenshot 2: Task UPDATE & DELETE [attach image below] Screenshot 3: Session Routes (CREATE, READ) [attach image below] Screenshot 4: MongoDB Atlas Collections [attach image below] ``` ### Important Notes: - **Deadline:** Tuesday, November 18, 2024 by 11:59 PM - **All submissions via Blackboard** - do not email screenshots - **GitHub link must work** - test it in an incognito window before submitting --- Grading Rubric (4 points total) ------------------------------- This lab is worth **4 points** and graded based on **screenshots submitted to Blackboard**. ### Submission Requirements Submit the following to Blackboard: #### 1. GitHub Repository Link - Must be a clickable link to your completed Lab 7 repository - Repository must contain all code (models, server.js, package.json) #### 2. Four Screenshots --- ### Screenshot 1: Task CRUD Routes (1.5 points) **Requirements:** - Thunder Client showing **POST** request creating a task with response - Thunder Client showing **GET** all tasks (must show at least 2 tasks in array) - Thunder Client showing **GET** one task by ID with response **Options:** - Take 3 separate screenshots (one per route) - OR take one screenshot showing all three in Thunder Client history **Grading:** - All 3 routes work correctly (201/200 status, valid JSON responses) = **1.5 points** - 2 routes work = **1 point** - 1 route works = **0.5 points** - None work or screenshot missing = **0 points** --- ### Screenshot 2: Task UPDATE & DELETE (1 point) **Requirements:** - Thunder Client showing **PUT** request updating a task (e.g., marking complete or changing title) - Thunder Client showing **DELETE** request removing a task **Must show:** - Both requests with success responses (200 status codes) - Updated/deleted data visible in response body **Grading:** - Both routes work correctly = **1 point** - Only one route works = **0.5 points** - Neither works or screenshot missing = **0 points** --- ### Screenshot 3: Session Routes (1 point) **Requirements:** - Thunder Client showing **POST** request creating a Pomodoro session - Thunder Client showing **GET** request retrieving all sessions (must show at least 1 session) **Must show:** - Session includes taskId, duration, startTime fields - Proper status codes (201 for POST, 200 for GET) **Grading:** - Both routes work correctly = **1 point** - Only one route works = **0.5 points** - Neither works or screenshot missing = **0 points** --- ### Screenshot 4: MongoDB Atlas Verification (0.5 points) **Requirements:** - MongoDB Atlas "Browse Collections" view showing BOTH: - `tasks` collection with at least 2 documents visible - `sessions` collection with at least 1 document visible **How to get this screenshot:** 1. Go to MongoDB Atlas 2. Click "Browse Collections" on your cluster 3. Navigate to show both collections with data 4. Take screenshot showing collection names and document counts **Grading:** - Both collections visible with data = **0.5 points** - Collections missing, empty, or screenshot missing = **0 points** ### What Good Screenshots Look Like: **Thunder Client screenshots should show:** - The HTTP method (GET, POST, PUT, DELETE) - The full URL (`http://localhost:3001/api/tasks`) - Request body (for POST/PUT) in JSON format - Response body showing returned data - Status code (200, 201, etc.) **MongoDB Atlas screenshot should show:** - Collection names in sidebar (`tasks`, `sessions`) - Document count for each collection - At least one document visible with fields --- ### Submission Format **In Blackboard text box:** ``` GitHub Repository: [paste clickable link here] Screenshot 1: Task CRUD Routes [attach image] Screenshot 2: Task UPDATE & DELETE [attach image] Screenshot 3: Session Routes [attach image] Screenshot 4: MongoDB Atlas Collections [attach image] ``` --- Code Quality Checklist ---------------------- Before submitting, verify: - [ ] All routes use `async/await` properly - [ ] All routes have `try/catch` error handling - [ ] Status codes are correct (201 for create, 404 for not found, etc.) - [ ] Mongoose models export correctly - [ ] `.env` is in `.gitignore` - [ ] Code is properly indented and readable - [ ] Variable names are descriptive - [ ] No console.log() statements left in final code - [ ] Server starts without errors - [ ] All tests pass in Thunder Client --- Common Issues & Solutions ------------------------- ### "Cannot find module './models/Task'" **Problem:** Path is wrong or file doesn't exist **Solution:** - Check that `models/Task.js` exists - Check that path in `require()` matches file location - Use `./models/Task` (relative path with `./`) --- ### "ValidationError: Path `title` is required" **Problem:** Trying to create task without required field **Solution:** - Check request body in Thunder Client - Make sure `title` field is included - Check that field names match schema exactly --- ### "Cast to ObjectId failed" **Problem:** Invalid ID format in route parameter **Solution:** - MongoDB IDs are 24 hex characters - Make sure you're copying the full `_id` from a document - Check for extra spaces or characters --- ### "Cannot set headers after they are sent" **Problem:** Multiple `res.json()` or `res.status()` calls **Solution:** - Use `return` before `res.json()` in error cases - Only send ONE response per request ```javascript // ❌ Wrong - sends two responses if (!task) { res.status(404).json({message: 'Not found'}); } res.json(task); // This still runs! // ✅ Correct - returns after error if (!task) { return res.status(404).json({message: 'Not found'}); } res.json(task); // Only runs if task exists ``` --- ### Server connects but routes don't work **Problem:** Routes defined after `app.listen()` **Solution:** - Put all route definitions BEFORE `app.listen()` - Order matters in Express! --- Testing Strategy ---------------- **Build incrementally! Don't write all routes then test.** ### Recommended Order: 1. **Set up schemas** (Task and Session models) 2. **Test connection** (`npm start` shows "Connected to MongoDB") 3. **Build POST /api/tasks** → Test immediately with Thunder Client 4. **Build GET /api/tasks** → Test immediately 5. **Build GET /api/tasks/:id** → Test immediately 6. **Build PUT /api/tasks/:id** → Test immediately 7. **Build DELETE /api/tasks/:id** → Test immediately 8. **Build POST /api/sessions** → Test immediately 9. **Build GET /api/sessions** → Test immediately 10. **Verify in MongoDB Atlas** (see below) 11. **Take screenshots for submission** 12. **Commit to GitHub** **Why this order?** You can't test GET, PUT, or DELETE without creating data first (POST). Test each route immediately after writing it to catch errors early! --- Verifying Data in MongoDB Atlas ------------------------------- **This is CRITICAL for Screenshot 4 and understanding persistence!** ### How to Check Your Data: 1. **Go to MongoDB Atlas** (https://cloud.mongodb.com) 2. **Click on your cluster** (usually called Cluster0) 3. **Click "Browse Collections" button** (big green button) 4. **Navigate to your database:** - If you didn't specify a database name in your connection string, it's called `test` - Look for collections named `tasks` and `sessions` 5. **Click on each collection** to see your documents ### What You Should See: **Tasks collection:** ``` { "_id": ObjectId("691619689ab79f70c8c8a890"), "title": "Write research paper", "completed": false, "createdAt": ISODate("2024-11-13T14:30:00.000Z"), "updatedAt": ISODate("2024-11-13T14:30:00.000Z"), "__v": 0 } ``` **Sessions collection:** ``` { "_id": ObjectId("691619689ab79f70c8c8a999"), "taskId": ObjectId("691619689ab79f70c8c8a890"), "duration": 1500, "completed": true, "startTime": ISODate("2024-11-13T14:00:00.000Z"), "createdAt": ISODate("2024-11-13T14:30:00.000Z"), "updatedAt": ISODate("2024-11-13T14:30:00.000Z"), "__v": 0 } ``` ### Live Testing: **Try this to see persistence in action:** 1. **Create a task** with Thunder Client (POST) 2. **Refresh MongoDB Atlas** → See it appear! 3. **Update the task** with Thunder Client (PUT) 4. **Refresh MongoDB Atlas** → See the change! 5. **Delete the task** with Thunder Client (DELETE) 6. **Refresh MongoDB Atlas** → See it disappear! This proves your data is actually being saved to the database, not just held in memory. Even if you stop your Express server, the data stays in MongoDB Atlas! 🎉 --- Resources --------- ### Documentation - [Mongoose Guide](https://mongoosejs.com/docs/guide.html) - Schema definitions - [Mongoose Queries](https://mongoosejs.com/docs/queries.html) - Find, update, delete - [Express Routing](https://expressjs.com/en/guide/routing.html) - Route parameters - [MongoDB CRUD](https://www.mongodb.com/docs/manual/crud/) - Database operations ### Today's Lecture Code - Books example from Day 19: - Slides: https://hackmd.io/@profmoore/Day19-APIs - Code: https://github.com/merriekay/cs195-full-stack-notes/blob/main/day19-APIs/server.js - Reference this code when building your routes! FAQs ---- **Q: Do I need to build a frontend for this lab?** A: **No!** This lab is backend-only. Test everything with Thunder Client. You already have a working Lab 6 frontend (Pomodoro timer with task list), but connecting them is NOT required for this lab. **Q: How do I know if my routes are actually working?** A: Two ways: (1) Thunder Client shows success responses with data, (2) MongoDB Atlas shows the data saved in your collections. **Q: What if I can't connect to MongoDB?** A: Check three things: (1) Network Access in Atlas has `0.0.0.0/0` whitelisted, (2) Your `.env` file has the correct connection string, (3) Your password doesn't have special characters (or is URL-encoded). **Q: Can I add more fields to my schemas?** A: Yes! Once you have the basic requirements working, you can add fields like `priority`, `dueDate`, `tags`, or anything else. The same CRUD patterns will work. **Q: Should I do the extensions?** A: Only if you finish the core requirements and want extra practice. Extensions don't give extra credit but are good preparation for Project #2. **Q: Can I test my routes in the browser?** A: GET routes yes (just paste the URL), but POST/PUT/DELETE routes need Thunder Client or similar tools. Browsers only do GET requests by default. **Q: Do I need to deploy this?** A: No deployment required for Lab 7. **Q: What if I finish early?** A: Try extensions, help classmates, or start connecting your Lab 6 frontend to this backend! **Q: Can I use different field names in my schema?** A: Stick to the provided schemas for consistency. Your frontend expects these field names. --- **Good luck! You've got this! 💪** Remember: The pattern from Day 19 works for ANY resource. If you understood the Books example, you can build these Task and Session routes. Same structure, different data. --- **Questions?** Come to office hours or ask in class on Tuesday!