Skip to content
Open
Show file tree
Hide file tree
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
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@
## 2024-05-18 - Concurrent Fetching with Owner IDs in Share Links
**Learning:** In the share system, endpoints processing a share code typically suffer from waterfall latency by first loading target entity details (like a project) and then querying its associated elements (like project lists) using the entity's owner ID (`userId`). However, the `shareLink` object already contains the `userId` field (representing the owner's ID).
**Action:** Always leverage the existing owner's ID within `shareLink` to bypass sequential dependencies and fetch parent entities (like projects) concurrently with their child elements (like user lists) using `Promise.all`.

## 2024-05-19 - Derive Subsets In-Memory to Reduce API Calls
**Learning:** Requesting a global collection (e.g., all lists for a user) and a subset of that same collection (e.g., lists for a specific project) as separate API calls creates redundant backend database queries and increases network payload unnecessarily.
**Action:** When both a global collection and its subset are needed on the frontend simultaneously, fetch only the global collection and derive the subset in-memory using array filtering. This eliminates redundant API endpoints and backend load.
16 changes: 7 additions & 9 deletions src/app/projects/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,17 @@ export default function ProjectDetailPage({ params }: { params: Promise<{ id: st
setIsLoading(true);

// OPTIMIZATION: Execute independent network requests and JSON parsing concurrently
// using Promise.all. This prevents a 3-step waterfall, reducing Time to First Byte
// using Promise.all. This prevents a waterfall, reducing Time to First Byte
// (TTFB) and overall load time significantly on this detail page.
const [projectRes, listsRes, allListsRes] = await Promise.all([
// We fetch the project details and all the user's lists, deriving the project
// lists in-memory to prevent a redundant backend query.
const [projectRes, allListsRes] = await Promise.all([
fetch(`/api/projects/${projectId}`),
fetch(`/api/projects/${projectId}/lists`),
fetch("/api/lists"),
]);

const [projectResult, listsResult, allListsResult] = await Promise.all([
const [projectResult, allListsResult] = await Promise.all([
projectRes.json(),
listsRes.json(),
allListsRes.json(),
]);

Expand All @@ -81,12 +81,10 @@ export default function ProjectDetailPage({ params }: { params: Promise<{ id: st
setEditName(projectResult.data.name);
setEditDescription(projectResult.data.description || "");

if (listsResult.success) {
setLists(listsResult.data);
}

if (allListsResult.success) {
setAllLists(allListsResult.data);
// Derive the project-specific lists in-memory
setLists(allListsResult.data.filter((list: List) => list.projectId === projectId));
}
} catch (err) {
console.error("Error fetching project:", err);
Expand Down
Loading