Deduplicate getPost calls in post page (generateMetadata + prefetch)

Problem

Post pages (/@author/permlink, /hive-123/@author/permlink) call getPost() twice during SSR - once in generateMetadata() and once in the page prefetch. This results in duplicate bridge.get_post API calls.

Root Cause

In apps/blog/app/[param]/[p2]/[permlink]/layout.tsx:

// generateMetadata() - Line ~28-31
export async function generateMetadata({ params }: PostPageLayoutProps): Promise<Metadata> {
  // ...
  const post = await getPost(author, permlink, DEFAULT_OBSERVER);  // FIRST CALL
  // ... uses post for OG metadata (title, description, image)
}

In apps/blog/app/[param]/[p2]/[permlink]/page.tsx:

// Page component - Line ~36-42
export default async function PostPage({ params, searchParams }: PostPageProps) {
  // ...
  await queryClient.prefetchQuery({
    queryKey: ['postData', username, permlink],
    queryFn: () => getPost(username, permlink, observer)  // SECOND CALL (same post!)
  });
  // ...
}

Evidence from Production Logs

Error logs from blog.openhive.network show multiple get_post calls in generateMetadata for the same page request:

Error in generateMetadata: bridge.get_post @author/permlink
Error in generateMetadata: bridge.get_post @author/permlink2

This confirms the duplication is happening in production.

Why React Query Doesn't Deduplicate This

  1. generateMetadata() in layout.tsx runs independently
  2. It calls getPost() directly (not through React Query)
  3. The page.tsx then calls prefetchQuery() with the same function
  4. No request-level deduplication between layout and page

Impact

  • 1 duplicate API call per post page visit:
    • bridge.get_post called twice for same author/permlink
  • Adds ~100-300ms latency per post page
  • Increases API load

Proposed Fix

Use Next.js cache() to deduplicate at the request level:

// In apps/blog/lib/cached-api.ts
import { cache } from 'react';
import { getPost } from '@transaction/lib/bridge-api';

export const getPostCached = cache(getPost);

Then use getPostCached in both layout.tsx generateMetadata and page.tsx prefetch.

Alternative Consideration

Could also restructure to only fetch in one place:

  • Fetch only in page, use for both rendering and metadata
  • But this requires restructuring Next.js metadata generation

The cache() approach is simpler and doesn't require architectural changes.

  • #794 (closed) - Deduplicate getAccountFull calls in profile page
  • #793 (closed) - Remove duplicate community data prefetching
  • #790 (closed) - Remove unnecessary SSR prefetch for user subscriptions
  • #764 - Current API calls are sub-optimal for denser needs
  • NOTES/05-api-call-comparison-results.md - Full API comparison analysis