Skip to content

Conversation

jonkafton
Copy link
Contributor

@jonkafton jonkafton commented Oct 15, 2025

What are the relevant tickets?

Description (What does it do?)

  • Reuses a single query client instance for API calls made during a request lifecycle
    • Duplicate requests in prefetching and metadata generation are only made once.
    • Retry logic is applied consistently and in one place.
  • Uses the shared query client in metadata generation for retry.
    • Direct calls into the API client are replaced with queryClient.fetchQuery() and enforced with ESLint rules.
  • Provides a safeGenerateMetadata utility that catches errors. Uncaught errors here were responsible for the error page being shown as a result of the non-serializable error being passed to client components.
    • Enforce use with an ESLint rule.

The approach is to reuse the QueryClient for all API requests made from the server (page server components, generateMetadata, client components during the SSR render phase). The QueryClient contains our retry logic, so we able to specify it in one place. React's server component cache

Reusing a QueryClient instance and therefore its QueryCache enables us, to share a single instance across a request lifecycle using React cache. This provides a cache layer that prevents duplicate API requests made during SSR prefetching and metadata generation. The instance is memoized and invalidated for each server request.

How can this be tested?

Request caching

  • We need to run Next.js in production mode as behaviors are different in development mode.
  • Run services without the watch container COMPOSE_PROFILES=backend,apisix,keycloak docker compose up (or use local proxy described below).
  • Build the Next.js container, cd frontends/main && yarn build
  • Run with PORT=8062 yarn start
  • Monitor the apisix logs, docker compose logs -f apigateway
  • Load a channel page, e.g. http://open.odl.local:8062/c/unit/ocw
  • The server makes a prefetch request for the channel detail and another to generate metadata.
    • Check that only one request has been made to the channel detail endpoint from the server, e.g. GET /api/v0/channels/type/unit/ocw/
  • Check that a single request is made for each refresh.

Retry mechanism

  • To test retry I've been using a proxy script that emulates errors responses: https://gist.github.com/jonkafton/278f80fcf5219f4af53a0ad0257bdef2. This is set to error for calls from the server to GET /api/v0/channels/type/unit/ocw/ for the first two attempts, but edit accordingly.
  • The script proxies to RC, so we can stop the docker services.
  • Drop the 2 files in a directory and run yarn && yarn proxy:watch.
  • Load the channel page, http://open.odl.local:8062/c/unit/ocw
    • Check the proxy logs to make sure that the server makes two requests and succeeds on the third.
    • Check that the page loads correctly.
    • The page meta tags should fall back to default values, "MIT Learn" (og:title) and Learn with MIT (og:description).
  • Edit proxy.ts to continue to error after 3 attempts / indefinitely.
    • Check that the page loads correctly, falling back to client side API call.

Additional Context

I also looked at using native fetch on the Axios instance - it can be set as the adapter on the config. The goal is that we make use of Next.js' data cache and route segment config for caching rules, however the fetch adapter is not compatible with React Query - the retry mechanism is called, but no actual requests are made; nor is it compatible with Next.js - requests are persisted (in ./main/.next/cache/fetch-cache), however config is ignored and all requests have a max-age of 1 year, which is not what we want.

@jonkafton jonkafton marked this pull request as draft October 15, 2025 21:08
@jonkafton jonkafton marked this pull request as ready for review October 16, 2025 20:43
@jonkafton jonkafton marked this pull request as draft October 17, 2025 20:20
Copy link

github-actions bot commented Oct 20, 2025

OpenAPI Changes

Show/hide No detectable change.

Unexpected changes? Ensure your branch is up-to-date with main (consider rebasing).

@jonkafton jonkafton marked this pull request as ready for review October 20, 2025 20:40
@jonkafton jonkafton added the Needs Review An open Pull Request that is ready for review label Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs Review An open Pull Request that is ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant