Combine SSR, identity and caching
Handle public content and request-scoped identity separately rather than storing sessions in a shared reader.
Public content client
Create an anonymous reader with an absolute endpoint and explicit timeout. Do not attach personal Cookies. React cache may memoize one render request, not become a cross-request content cache.
TypeScript
import type { Shell } from '@mosaic-base/shell';
import type { ArticleSDK, SiteSDK, I18nSDK } from '@mosaic-base/client-sdk';
import type { AuthClient, ServerAuthClient } from '@mosaic-base/auth-sdk';
// Declare once in your application's type contract, never import SDK implementations.
declare module '@mosaic-base/shell/contracts' {
interface SdkRegistry {
'site.config': Pick<SiteSDK, 'getConfig'>;
'content.articles': Pick<ArticleSDK, 'list' | 'getBySlug' | 'getPreview' | 'search'>;
'i18n.messages': Pick<I18nSDK, 'load' | 't' | 'clearCache' | 'refresh'>;
'auth.client': AuthClient | ServerAuthClient;
}
}
import type { Bootstrap } from '@mosaic-base/shell';
import { startShell } from '@mosaic-base/shell/bootstrap';
import { createNodeModuleLoader } from '@mosaic-base/shell/node';
const loader = createNodeModuleLoader(); // modules may be shared, user SDK instances may not
export function createPublicReader(bootstrap: Bootstrap, apiBaseUrl: string) {
return startShell({ bootstrap, loader, host: { apiBaseUrl,
capabilityOptions: { 'i18n.messages': { cacheTtlMs: 0 } } } });
}
// Read protected Node bootstrap with server-sdk and explicit grant/owner.
// Create/dispose one Shell per SSR operation/request; public HTTP omits Cookie.
// Pass that startup configuration's browser projection to hydration.Request-scoped authentication
Create the auth client from the current request Headers. The host refreshes Cookies in a writable response before server consumption. Never store one user’s identity in a module-global client.
TypeScript
import type { Shell } from '@mosaic-base/shell';
import type { ArticleSDK, SiteSDK, I18nSDK } from '@mosaic-base/client-sdk';
import type { AuthClient, ServerAuthClient } from '@mosaic-base/auth-sdk';
// Declare once in your application's type contract, never import SDK implementations.
declare module '@mosaic-base/shell/contracts' {
interface SdkRegistry {
'site.config': Pick<SiteSDK, 'getConfig'>;
'content.articles': Pick<ArticleSDK, 'list' | 'getBySlug' | 'getPreview' | 'search'>;
'i18n.messages': Pick<I18nSDK, 'load' | 't' | 'clearCache' | 'refresh'>;
'auth.client': AuthClient | ServerAuthClient;
}
}
export async function readRequestUser(shell: Shell) {
const auth = await shell.getSdk('auth.client');
return auth.getUser();
}
// Create the request Shell with only auth.client's Cookie policy and realmKey option.
// Dispose in the request owner. Node SDK is read-only; renewal is a writable boundary.Real translation behavior
The translation page separates SSR and CSR reads. Accept only responses for the current selection; cleared caches cannot be repopulated by older requests. Keep built-in copy and retry after failures.