CSR Cache & Next.js

The browser cache is used with Core's createCsrCache, and /next's callApiSsrCache is added only when Next.js server Data Cache is needed.


The cache wrapper is also part of commonService.

The callApiClientCache/callApiServerCache code on this page explains only the cache part of the project-wide HTTP adapter. Since the generated service can be configured to import this function, it is clearer to first look at the overall structure of rxjsHttpService.ts/commonService.ts to understand the position and role of each wrapper.

CSR Cache (Client Cache)

createCsrCache<CacheName>()provides callApiCsrCache(callApiFn, serviceArgs, cacheOptions) and removeCsrCache(cacheName).

ts
import {
  createCsrCache,
  type ServiceArguments,
} from "@byeolnaerim/typed-rx-http";
import type { paths as Paths } from "./@types/ApiTypes";

type CacheNames = "adminList" | "profile";

const csrCache = createCsrCache<CacheNames>();

export const callApiClientCache = <R>(
  args: ServiceArguments<Paths, any, any, R>,
  cache: {
    cacheName: CacheNames;
    cacheTime: number;
    cacheSize?: number;
  },
) => csrCache.callApiCsrCache(
  client.callApi,
  args as any,
  cache as any,
);

csrCache.removeCsrCache("adminList");
csrCache.removeCsrCache("any-prefix");

In the server environment, it calls the original callApi without creating a CSR cache map. In the browser, it shares the Observable results of the same cache key using shareReplay.

Next.js Adapter: callApiSsrCache

This is an SSR helper that uses Next.js's next/cache (unstable_cache). If it's a GET request and cacheTime > 0, it uses force-cache + revalidate; otherwise, it executes with no-store.

  • You can inject per-request Cookie/Authorization using headersProvider.
  • If onServer401 exists at 401, it will be executed.
ts
import type { paths as Paths } from "./@types/ApiTypes";
import { firstValueFrom } from "rxjs";
import {
  callApiSsrCache,
  redirectToUnauthorizedOnServer401,
} from "@byeolnaerim/typed-rx-http/next";

const apiUrl = process.env.NEXT_PUBLIC_API_URL!;

const headersProvider = async () => {
  const { cookies } = await import("next/headers");
  const cookieStore = await cookies();

  const cookieHeader = cookieStore
    .getAll()
    .map((c) => `${c.name}=${c.value}`)
    .join("; ");

  const accessToken = cookieStore.get("accessToken")?.value;

  return {
    ...(cookieHeader ? { Cookie: cookieHeader } : {}),
    ...(accessToken
      ? { Authorization: `Bearer ${accessToken}` }
      : {}),
  };
};

const obs$ = callApiSsrCache<Paths, { ok: true }, "adminList">({
  baseUrl: apiUrl,
  serviceArguments: { url: "/api/secure", method: "get" },
  cacheForService: { cacheName: "adminList", cacheTime: 10_000 },
  headersProvider,
  onServer401: redirectToUnauthorizedOnServer401,
});

const res = await firstValueFrom(obs$);

Do not cache user-specific data.

When bundling browser/server into a single project wrapper

In the shared service, the browser can branch to createCsrCache, and the server can branch to callApiSsrCache. This is a project configuration pattern and not a required structure of Core itself.

commonService.ts
ts
const csrCache = createCsrCache<"memberList" | "profile">();

export const callApiClientCache = (
  serviceArguments,
  cacheForService,
) => csrCache.callApiCsrCache(
  callApi,
  serviceArguments,
  cacheForService,
);

export const removeCsrCache = csrCache.removeCsrCache;
commonService.ts
ts
export const callApiServerCache = (serviceArguments, cacheForService) => {
  if (typeof window !== "undefined") {
    return callApiClientCache(serviceArguments, cacheForService);
  }

  return from(
    import("@byeolnaerim/typed-rx-http/next"),
  ).pipe(
    mergeMap(({ callApiSsrCache }) =>
      callApiSsrCache({
        baseUrl: process.env.NEXT_PUBLIC_API_URL || "",
        serviceArguments,
        cacheForService,
        headersProvider,
      }),
    ),
  );
};

When using the cache wrapper in the auto-generated service, connect this wrapper only to endpoints that need cache functionality, while using the regular callApi for the rest.

© 2026 Byeolnaerim. All rights reserved.IntroductionPrivacy Policy