From eb99ea9d467b0ec8ff675518109b7eb36dab7eb0 Mon Sep 17 00:00:00 2001 From: Ronn Joshua <53202469+ronnjoshua@users.noreply.github.com> Date: Mon, 16 Mar 2026 18:45:43 +0800 Subject: [PATCH 1/2] Implement todo API --- backend/index.js | 7 +++---- backend/models/todo.js | 38 ++++++++++++++++++++++++++++++++++++++ backend/package.json | 3 ++- backend/routes/todos.js | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 backend/models/todo.js create mode 100644 backend/routes/todos.js diff --git a/backend/index.js b/backend/index.js index ff6a6a3b..41db72df 100644 --- a/backend/index.js +++ b/backend/index.js @@ -1,11 +1,10 @@ const express = require("express"); const app = express(); const PORT = process.env.PORT || 4000; +const todosRouter = require('./routes/todos'); -// Basic route -app.get("/", (req, res) => { - res.send("Hello from Express!"); -}); +app.use(express.json()); +app.use('/api/todos', todosRouter); // Start server app.listen(PORT, () => { diff --git a/backend/models/todo.js b/backend/models/todo.js new file mode 100644 index 00000000..2fb88ed7 --- /dev/null +++ b/backend/models/todo.js @@ -0,0 +1,38 @@ +let todos = []; +let nextId = 1; + +function getAllTodos() { + return todos; +} + +function getTodoById(id) { + return todos.find(t => t.id === id); +} + +function createTodo(title) { + const todo = { id: nextId++, title, completed: false, createdAt: new Date().toISOString() }; + todos.push(todo); + return todo; +} + +function updateTodo(id, data) { + const todo = todos.find(t => t.id === id); + if (!todo) return null; + if (data.title !== undefined) todo.title = data.title; + if (data.completed !== undefined) todo.completed = data.completed; + return todo; +} + +function deleteTodo(id) { + const index = todos.findIndex(t => t.id === id); + if (index === -1) return null; + return todos.splice(index, 1)[0]; +} + +module.exports = { + getAllTodos, + getTodoById, + createTodo, + updateTodo, + deleteTodo +}; \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index c85981fa..2846e01f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,6 +5,7 @@ "start": "node index.js" }, "dependencies": { - "express": "^4.18.2" + "body-parser": "^1.20.3", + "express": "^4.22.1" } } diff --git a/backend/routes/todos.js b/backend/routes/todos.js new file mode 100644 index 00000000..a546bcf3 --- /dev/null +++ b/backend/routes/todos.js @@ -0,0 +1,34 @@ +const express = require('express'); +const router = express.Router(); +const Todo = require('../models/todo'); + +router.get('/', (req, res) => { + res.json(Todo.getAllTodos()); +}); + +router.get('/:id', (req, res) => { + const todo = Todo.getTodoById(parseInt(req.params.id)); + if (!todo) return res.status(404).json({ error: 'Todo not found' }); + res.json(todo); +}); + +router.post('/', (req, res) => { + const { title } = req.body; + if (!title || typeof title !== 'string') return res.status(400).json({ error: 'Title required' }); + const newTodo = Todo.createTodo(title); + res.status(201).json(newTodo); +}); + +router.put('/:id', (req, res) => { + const updated = Todo.updateTodo(parseInt(req.params.id), req.body); + if (!updated) return res.status(404).json({ error: 'Todo not found' }); + res.json(updated); +}); + +router.delete('/:id', (req, res) => { + const deleted = Todo.deleteTodo(parseInt(req.params.id)); + if (!deleted) return res.status(404).json({ error: 'Todo not found' }); + res.json(deleted); +}); + +module.exports = router; \ No newline at end of file From 41a1433952c0ce950aee9d5ba3aab231b529938e Mon Sep 17 00:00:00 2001 From: Ronn Joshua <53202469+ronnjoshua@users.noreply.github.com> Date: Mon, 16 Mar 2026 18:47:46 +0800 Subject: [PATCH 2/2] Implement todo API --- package-lock.json | 72 +++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 43 deletions(-) diff --git a/package-lock.json b/package-lock.json index c61d591b..adc12a00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,8 @@ "backend": { "version": "1.0.0", "dependencies": { - "express": "^4.18.2" + "body-parser": "^1.20.3", + "express": "^4.22.1" } }, "frontend": { @@ -9830,38 +9831,39 @@ "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==" }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", + "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.13.0", + "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", + "send": "~0.19.0", + "serve-static": "~1.16.2", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -9904,35 +9906,19 @@ "node": ">= 0.8" } }, - "node_modules/express/node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "node_modules/express/node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "license": "BSD-3-Clause", "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "side-channel": "^1.1.0" }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/express/node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/fast-deep-equal": {