Skip to content

Settings

@granit/settings provides framework-agnostic types and API functions for application settings — mirroring Granit.Settings on the .NET backend. Settings follow a cascade: User → Tenant → Global → Config → Default. Deleting a user-level setting makes the tenant or global value take over.

@granit/react-settings wraps these into a provider and TanStack Query hooks with automatic cache invalidation on mutations.

Peer dependencies: axios, react ^19, @tanstack/react-query ^5

  • Directory@granit/settings/ Setting types, scope enum, API functions, well-known names (framework-agnostic)
    • @granit/react-settings SettingsProvider, query/mutation hooks
PackageRoleDepends on
@granit/settingsDTOs, SettingScope, API functions, SETTING_NAMESaxios
@granit/react-settingsSettingsProvider, useSetting, useSettings, useUpdateSetting, useDeleteSetting@granit/settings, @tanstack/react-query, react
import { SettingsProvider } from '@granit/react-settings';
import { api } from './api-client';
function App({ children }) {
return (
<SettingsProvider config={{ client: api }}>
{children}
</SettingsProvider>
);
}
type SettingScope = 'user' | 'global' | 'tenant';
interface SettingValueResponse {
name: string;
value: string | null;
}
type SettingsMap = Record<string, string | null>;
interface UpdateSettingValueRequest {
value: string | null;
}
const SETTING_NAMES = {
PREFERRED_CULTURE: 'Granit.Localization.PreferredCulture',
PREFERRED_TIMEZONE: 'Granit.Timing.PreferredTimezone',
} as const;
FunctionEndpointDescription
fetchSettings(client, basePath, scope)GET /settings/{scope}All visible settings for a scope
fetchSetting(client, basePath, scope, name)GET /settings/{scope}/{name}Single setting by name
updateSetting(client, basePath, scope, name, request)PUT /settings/{scope}/{name}Create or update a setting
deleteSetting(client, basePath, scope, name)DELETE /settings/{scope}/{name}Delete (reset) — cascade takes over
interface SettingsProviderProps {
readonly config: {
readonly client: AxiosInstance;
readonly basePath?: string;
readonly queryKeyPrefix?: readonly string[];
};
readonly children: ReactNode;
}
<SettingsProvider config={{ client: api }}>
{children}
</SettingsProvider>

Fetches all visible settings for a scope.

function useSettings(
scope: SettingScope,
options?: { enabled?: boolean }
): UseQueryResult<SettingsMap>;

Fetches a single setting by name.

function useSetting(
scope: SettingScope,
name: string,
options?: { enabled?: boolean }
): UseQueryResult<SettingValueResponse>;

Creates or updates a setting value. Invalidates scope and individual setting queries on success.

interface UseUpdateSettingReturn {
readonly update: (name: string, value: string | null) => void;
readonly updateAsync: (name: string, value: string | null) => Promise<void>;
readonly isPending: boolean;
readonly error: Error | null;
}

Deletes a setting so the cascade takes over. Invalidates queries on success.

interface UseDeleteSettingReturn {
readonly remove: (name: string) => void;
readonly removeAsync: (name: string) => Promise<void>;
readonly isPending: boolean;
readonly error: Error | null;
}
graph LR
    User["User"] --> Tenant["Tenant"]
    Tenant --> Global["Global"]
    Global --> Config["Config"]
    Config --> Default["Default"]

When a setting is read, the first non-null value in the cascade wins. Deleting a user-level override makes the tenant or global value visible again.

CategoryKey exportsPackage
TypesSettingScope, SettingValueResponse, SettingsMap, UpdateSettingValueRequest@granit/settings
ConstantsSETTING_NAMES@granit/settings
API functionsfetchSettings(), fetchSetting(), updateSetting(), deleteSetting()@granit/settings
ProviderSettingsProvider, useSettingsConfig(), buildSettingsQueryKey()@granit/react-settings
Query hooksuseSettings(), useSetting()@granit/react-settings
Mutation hooksuseUpdateSetting(), useDeleteSetting()@granit/react-settings