Skip to content

perf: dedupe getShared with React cache and parallelize updates page fetches#151

Draft
hugodemenez wants to merge 1 commit into
betafrom
cursor/dedupe-shared-parallel-updates-b44e
Draft

perf: dedupe getShared with React cache and parallelize updates page fetches#151
hugodemenez wants to merge 1 commit into
betafrom
cursor/dedupe-shared-parallel-updates-b44e

Conversation

@hugodemenez

Copy link
Copy Markdown
Owner

Problem

Two performance issues in the Next.js pages:

  1. Shared page (/shared/[slug])getShared(slug) was called twice per request: once in generateMetadata and again in the page component. Since getShared runs a Prisma transaction that queries the DB for trades, tick details, and groups (and also increments the view counter), the duplicate call doubled both latency and cost per page load, and inflated view counts.

  2. Updates page (/updates/[slug])getPost and getAdjacentPosts are independent data fetches but were running sequentially, adding unnecessary latency.

Solution

Shared page — React cache() deduplication

Wrapped getShared with React's cache() at module scope (getCachedShared). React cache deduplicates calls with the same arguments within a single server render, so generateMetadata and the page component now share a single DB call.

Updates page — Promise.allSettled parallelization

Replaced the sequential await getPost() / await getAdjacentPosts() with Promise.allSettled([...]) so both fetches run concurrently. allSettled preserves the existing error-handling behavior: a failed getAdjacentPosts gracefully falls back to { previous: null, next: null } without blocking the page render.

Changed files

  • app/[locale]/shared/[slug]/page.tsx
  • app/[locale]/(landing)/updates/[slug]/page.tsx
Open in Web Open in Cursor 

…fetches

- Wrap getShared in React.cache() on the shared page so generateMetadata
  and the page component share a single request per render instead of
  making two identical DB transactions (which also double-incremented
  the view counter).

- Replace sequential getPost + getAdjacentPosts calls on the updates
  page with Promise.allSettled to shave request latency.

Co-authored-by: Hugo Demenez <hugodemenez@users.noreply.github.com>
@vercel

vercel Bot commented May 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
deltalytix Ready Ready Preview, Comment May 2, 2026 9:59pm

@github-actions

github-actions Bot commented May 2, 2026

Copy link
Copy Markdown
Contributor

🤖 Automated Code Review

Here is a concise review based on /home/runner/work/deltalytix/deltalytix/pr.diff.


PR review summary

Updates page (updates/[slug]/page.tsx)

Good

  • Parallel I/O: Promise.allSettled runs getPost and getAdjacentPosts together, which should shorten time-to-render when both succeed.
  • Failures: Main post rejects → still matches prior behavior (console.error + notFound()). Adjacent rejects → still falls back to { previous: null, next: none }.

Considerations

  • Behavior under main-post failure: Before, adjacent posts were fetched only after a successful getPost. Now both run every time. If getPost rejects or resolves to “not found” quickly, you may still complete an unnecessary getAdjacentPosts call. Usually a minor cost; worth confirming that’s acceptable.
  • Readability: The IIFE around the rejection branch works but is heavier than a small let + if/else or a helper—pure style.

No security or correctness issues jumped out from the diff alone.


Shared page (shared/[slug]/page.tsx)

Good

  • cache(getShared) is appropriate for deduping the same slug between generateMetadata and the page in one request, avoiding duplicate work—a common Next.js/React pattern.

Minor

  • No newline at end of file: The diff removes the final newline; worth restoring for POSIX/tooling consistency.

TypeScript / React / errors

  • Types: Explicit cast on the adjacent fallback matches the earlier “make TS happy” pattern; acceptable if getAdjacentPosts’ return type isn’t inferred as { previous; next } already.
  • Error handling: Unchanged versus the old shared page component (still no local try/catch around the page fetch); metadata still catches and logs errors for metadata-only failures.

Verdict

  • Critical issues: None identified from this diff alone.

  • Main follow-ups: Optional—avoid extra adjacent fetch when the post cannot load (restore sequential or short-circuit if that matters operationally); restore EOF newline on shared/[slug]/page.tsx.


    Generated by Cursor CLI workflow

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR targets server-render performance in two Next.js App Router pages by reducing redundant data-fetch work and improving fetch concurrency.

Changes:

  • Deduplicates /shared/[slug] data fetching by wrapping getShared with React cache() so generateMetadata and the page reuse the same request-scoped result.
  • Parallelizes /updates/[slug] fetching of the main post and adjacent navigation data using Promise.allSettled.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
app/[locale]/shared/[slug]/page.tsx Adds a cached wrapper around getShared and uses it in both generateMetadata and the page component to avoid duplicate DB work / double view increments.
app/[locale]/(landing)/updates/[slug]/page.tsx Runs getPost and getAdjacentPosts concurrently to reduce end-to-end latency of the updates page render.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +143 to 151
const [postResult, adjacentPostsResult] = await Promise.allSettled([
getPost(slug, locale),
getAdjacentPosts(slug, locale),
]);

try {
post = await getPost(slug, locale);
} catch (postError) {
console.error("Error fetching post data:", postError);
if (postResult.status === "rejected") {
console.error("Error fetching post data:", postResult.reason);
notFound();
}
Comment on lines +143 to +146
const [postResult, adjacentPostsResult] = await Promise.allSettled([
getPost(slug, locale),
getAdjacentPosts(slug, locale),
]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants