Skip to content

Module Singleton

The Module Singleton pattern exploits the ES module cache to maintain a unique global state. A private module variable is shared by all importers — no static class or global registry required.

graph TD
    A["@granit/react-authentication"] -->|"setTokenGetter()"| Singleton["_tokenGetter\n(module variable)"]
    B["@granit/api-client\n(interceptor)"] -->|"reads"| Singleton
Singleton variablePackagePurpose
_tokenGetter@granit/api-clientBearer token getter shared between auth and HTTP client
Global log level@granit/loggerShared log level across all logger instances

Token management requires coordination between @granit/react-authentication (which obtains tokens from Keycloak) and @granit/api-client (which injects them into HTTP requests). A module singleton avoids direct package coupling — the auth package calls setTokenGetter() once during startup, and the interceptor reads it on every request.

// @granit/api-client — private module variable
let _tokenGetter: (() => Promise<string | undefined>) | null = null;
export function setTokenGetter(
getter: () => Promise<string | undefined>
): void {
_tokenGetter = getter;
}
// Axios interceptor reads _tokenGetter on every request
instance.interceptors.request.use(async (req) => {
if (_tokenGetter) {
const token = await _tokenGetter();
if (token) req.headers.Authorization = `Bearer ${token}`;
}
return req;
});

useKeycloakInit calls setTokenGetter() internally — the application never wires this manually.