Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions backend/todo-api/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const express = require('express');
const app = express();
const PORT = 3000;

// Middleware for parsing JSON
app.use(express.json());

// In-memory storage
let todos = [];
let nextId = 1;

// GET /api/todos - list all todos
app.get('/api/todos', (req, res) => {
res.json(todos);
});

// GET /api/todos/:id - get single todo
app.get('/api/todos/:id', (req, res) => {
const id = parseInt(req.params.id);
const todo = todos.find(t => t.id === id);

if (!todo) {
return res.status(404).json({ error: 'Todo not found' });
}

res.json(todo);
});

// POST /api/todos - create new todo
app.post('/api/todos', (req, res) => {
const { title, completed } = req.body;

// Validation
if (!title || typeof title !== 'string' || !title.trim()) {
return res.status(400).json({ error: 'Title is required and must be a non-empty string' });
}

const newTodo = {
id: nextId++,
title: title.trim(),
completed: completed === true, // default false unless explicitly true
createdAt: new Date().toISOString(),
};

todos.push(newTodo);
res.status(201).json(newTodo);
});

// PUT /api/todos/:id - update todo
app.put('/api/todos/:id', (req, res) => {
const id = parseInt(req.params.id);
const { title, completed } = req.body;
const todo = todos.find(t => t.id === id);

if (!todo) {
return res.status(404).json({ error: 'Todo not found' });
}

// Validation
if (title !== undefined && (typeof title !== 'string' || !title.trim())) {
return res.status(400).json({ error: 'Title must be a non-empty string if provided' });
}
if (completed !== undefined && typeof completed !== 'boolean') {
return res.status(400).json({ error: 'Completed must be a boolean value' });
}

if (title !== undefined) todo.title = title.trim();
if (completed !== undefined) todo.completed = completed;

res.json(todo);
});

// DELETE /api/todos/:id - delete todo
app.delete('/api/todos/:id', (req, res) => {
const id = parseInt(req.params.id);
const index = todos.findIndex(t => t.id === id);

if (index === -1) {
return res.status(404).json({ error: 'Todo not found' });
}

todos.splice(index, 1);
res.status(204).send(); // No content
});

// Generic error handler
app.use((err, req, res, next) => {
console.error('Unexpected error:', err);
res.status(500).json({ error: 'Internal server error' });
});

// Start server
app.listen(PORT, () => {
console.log(`🚀 Server running at http://localhost:${PORT}`);
});