Skip to content

Commit 475c81f

Browse files
author
claudfuen
committed
feat: enhance todo management with server-side fetching and API integration
- Added axios for API requests to streamline todo operations. - Implemented server-side fetching of todos in the Home component for improved performance. - Refactored API routes to simplify error handling and response structure. - Introduced a new service layer for todos to encapsulate API interactions. - Updated TodoApp component to utilize initial todos and provide refetch functionality. - Removed unnecessary debug information from API responses to clean up the codebase.
1 parent 47cfc64 commit 475c81f

File tree

10 files changed

+9838
-228
lines changed

10 files changed

+9838
-228
lines changed

apps/infra/index.ts.backup

Lines changed: 695 additions & 0 deletions
Large diffs are not rendered by default.

apps/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
},
2121
"dependencies": {
2222
"@t3-oss/env-nextjs": "^0.13.8",
23+
"axios": "^1.10.0",
2324
"dotenv": "^17.1.0",
2425
"drizzle-orm": "^0.44.2",
2526
"next": "15.3.5",

apps/web/src/app/api/todos/[id]/route.ts

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,22 @@ import { db, todos } from "@/db";
22
import { eq } from "drizzle-orm";
33
import { NextResponse } from "next/server";
44

5-
// PATCH /api/todos/[id] - Update a todo
6-
export async function PATCH(
5+
// PUT /api/todos/[id] - Update a todo
6+
export async function PUT(
77
request: Request,
88
{ params }: { params: Promise<{ id: string }> }
99
) {
1010
try {
1111
const { id: idParam } = await params;
1212
const id = parseInt(idParam);
1313
if (isNaN(id)) {
14-
return NextResponse.json({ error: "Invalid todo ID" }, { status: 400 });
14+
return NextResponse.json(
15+
{
16+
success: false,
17+
error: "Invalid todo ID"
18+
},
19+
{ status: 400 }
20+
);
1521
}
1622

1723
const body = await request.json();
@@ -38,14 +44,26 @@ export async function PATCH(
3844
.returning();
3945

4046
if (!updatedTodo) {
41-
return NextResponse.json({ error: "Todo not found" }, { status: 404 });
47+
return NextResponse.json(
48+
{
49+
success: false,
50+
error: "Todo not found"
51+
},
52+
{ status: 404 }
53+
);
4254
}
4355

44-
return NextResponse.json(updatedTodo);
56+
return NextResponse.json({
57+
success: true,
58+
data: updatedTodo,
59+
});
4560
} catch (error) {
4661
console.error("Failed to update todo:", error);
4762
return NextResponse.json(
48-
{ error: "Failed to update todo" },
63+
{
64+
success: false,
65+
error: "Failed to update todo"
66+
},
4967
{ status: 500 }
5068
);
5169
}
@@ -60,7 +78,13 @@ export async function DELETE(
6078
const { id: idParam } = await params;
6179
const id = parseInt(idParam);
6280
if (isNaN(id)) {
63-
return NextResponse.json({ error: "Invalid todo ID" }, { status: 400 });
81+
return NextResponse.json(
82+
{
83+
success: false,
84+
error: "Invalid todo ID"
85+
},
86+
{ status: 400 }
87+
);
6488
}
6589

6690
const [deletedTodo] = await db
@@ -69,14 +93,26 @@ export async function DELETE(
6993
.returning();
7094

7195
if (!deletedTodo) {
72-
return NextResponse.json({ error: "Todo not found" }, { status: 404 });
96+
return NextResponse.json(
97+
{
98+
success: false,
99+
error: "Todo not found"
100+
},
101+
{ status: 404 }
102+
);
73103
}
74104

75-
return NextResponse.json({ message: "Todo deleted successfully" });
105+
return NextResponse.json({
106+
success: true,
107+
message: "Todo deleted successfully"
108+
});
76109
} catch (error) {
77110
console.error("Failed to delete todo:", error);
78111
return NextResponse.json(
79-
{ error: "Failed to delete todo" },
112+
{
113+
success: false,
114+
error: "Failed to delete todo"
115+
},
80116
{ status: 500 }
81117
);
82118
}

apps/web/src/app/api/todos/route.ts

Lines changed: 15 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,25 @@
11
import { db, todos } from "@/db";
2-
import { count, desc } from "drizzle-orm";
2+
import { desc } from "drizzle-orm";
33
import { NextResponse } from "next/server";
44

5-
interface DebugStep {
6-
step: number;
7-
action?: string;
8-
result?: string;
9-
todoCount?: number;
10-
todosReturned?: number;
11-
todoId?: number;
12-
title?: string;
13-
message?: string;
14-
}
15-
16-
interface ErrorInfo {
17-
message: string;
18-
name: string;
19-
stack?: string;
20-
code?: string;
21-
detail?: string;
22-
hint?: string;
23-
}
24-
25-
interface DebugInfo {
26-
timestamp: string;
27-
steps: DebugStep[];
28-
environment?: {
29-
NODE_ENV: string | undefined;
30-
DATABASE_URL_exists: boolean;
31-
DATABASE_URL_length: number;
32-
};
33-
error?: ErrorInfo;
34-
}
35-
36-
// GET /api/todos - List all todos
5+
// GET /api/todos - List all todos (simplified, mainly for refetch after mutations)
376
export async function GET() {
38-
const debugInfo: DebugInfo = {
39-
timestamp: new Date().toISOString(),
40-
steps: [],
41-
environment: {
42-
NODE_ENV: process.env.NODE_ENV,
43-
DATABASE_URL_exists: !!process.env.DATABASE_URL,
44-
DATABASE_URL_length: process.env.DATABASE_URL?.length || 0,
45-
},
46-
};
47-
487
try {
49-
// Step 1: Test basic table access with count
50-
debugInfo.steps.push({
51-
step: 1,
52-
action: "Testing table access with count query",
53-
});
54-
const countResult = await db.select({ count: count() }).from(todos);
55-
const todoCount = countResult[0]?.count || 0;
56-
debugInfo.steps.push({
57-
step: 1,
58-
result: "success",
59-
todoCount,
60-
message: `Table accessible, found ${todoCount} todos`,
61-
});
62-
63-
// Step 2: Try to fetch actual todos
64-
debugInfo.steps.push({
65-
step: 2,
66-
action: "Fetching all todos with ordering",
67-
});
688
const allTodos = await db
699
.select()
7010
.from(todos)
7111
.orderBy(desc(todos.createdAt));
7212

73-
debugInfo.steps.push({
74-
step: 2,
75-
result: "success",
76-
todosReturned: allTodos.length,
77-
message: `Successfully fetched ${allTodos.length} todos`,
78-
});
79-
80-
console.log("Todos fetch successful:", debugInfo);
8113
return NextResponse.json({
8214
success: true,
8315
data: allTodos,
84-
debug: debugInfo,
8516
});
8617
} catch (error) {
87-
const errorInfo = {
88-
message: error instanceof Error ? error.message : String(error),
89-
name: error instanceof Error ? error.name : "Unknown",
90-
stack: error instanceof Error ? error.stack : undefined,
91-
code: (error as { code?: string })?.code,
92-
detail: (error as { detail?: string })?.detail,
93-
hint: (error as { hint?: string })?.hint,
94-
};
95-
96-
debugInfo.error = errorInfo;
97-
98-
console.error("Detailed error in todos fetch:", debugInfo);
99-
18+
console.error("Error fetching todos:", error);
10019
return NextResponse.json(
10120
{
10221
success: false,
10322
error: "Failed to fetch todos",
104-
message: errorInfo.message,
105-
debug: debugInfo,
106-
details: errorInfo,
10723
},
10824
{ status: 500 }
10925
);
@@ -112,80 +28,37 @@ export async function GET() {
11228

11329
// POST /api/todos - Create a new todo
11430
export async function POST(request: Request) {
115-
const debugInfo: DebugInfo = {
116-
timestamp: new Date().toISOString(),
117-
steps: [],
118-
};
119-
12031
try {
121-
debugInfo.steps.push({ step: 1, action: "Parsing request body" });
122-
const body = await request.json();
123-
const { title } = body;
124-
125-
if (!title || typeof title !== "string") {
32+
const { title } = await request.json();
33+
34+
if (!title?.trim()) {
12635
return NextResponse.json(
12736
{
12837
success: false,
129-
error: "Title is required and must be a string",
130-
debug: debugInfo,
38+
error: "Title is required",
13139
},
13240
{ status: 400 }
13341
);
13442
}
13543

136-
debugInfo.steps.push({
137-
step: 1,
138-
result: "success",
139-
title: title.trim(),
140-
message: "Request body parsed successfully",
141-
});
142-
143-
debugInfo.steps.push({
144-
step: 2,
145-
action: "Inserting new todo into database",
146-
});
14744
const [newTodo] = await db
14845
.insert(todos)
149-
.values({ title: title.trim() })
46+
.values({
47+
title: title.trim(),
48+
completed: false,
49+
})
15050
.returning();
15151

152-
debugInfo.steps.push({
153-
step: 2,
154-
result: "success",
155-
todoId: newTodo.id,
156-
message: `Todo created with ID ${newTodo.id}`,
52+
return NextResponse.json({
53+
success: true,
54+
data: newTodo,
15755
});
158-
159-
console.log("Todo creation successful:", debugInfo);
160-
return NextResponse.json(
161-
{
162-
success: true,
163-
data: newTodo,
164-
debug: debugInfo,
165-
},
166-
{ status: 201 }
167-
);
16856
} catch (error) {
169-
const errorInfo = {
170-
message: error instanceof Error ? error.message : String(error),
171-
name: error instanceof Error ? error.name : "Unknown",
172-
stack: error instanceof Error ? error.stack : undefined,
173-
code: (error as { code?: string })?.code,
174-
detail: (error as { detail?: string })?.detail,
175-
hint: (error as { hint?: string })?.hint,
176-
};
177-
178-
debugInfo.error = errorInfo;
179-
180-
console.error("Detailed error in todo creation:", debugInfo);
181-
57+
console.error("Error creating todo:", error);
18258
return NextResponse.json(
18359
{
18460
success: false,
18561
error: "Failed to create todo",
186-
message: errorInfo.message,
187-
debug: debugInfo,
188-
details: errorInfo,
18962
},
19063
{ status: 500 }
19164
);

0 commit comments

Comments
 (0)