Database-backed key-value application settings for NestJS with TypeORM.
This package provides a database-backed settings store for NestJS that lets you manage application configuration at runtime with typed access, in-memory caching, and auto-seeding.
Once installed, using it is as simple as:
const appName = await settingsService.get<string>("app.name", "MyApp");
await settingsService.set("app.theme", "dark");- Installation
- Quick Start
- Module Configuration
- Using the Service
- Setting Types
- Setting Groups
- Caching
- Auto-Seeding
- Events
- Configuration Options
- Testing
- Changelog
- Contributing
- Security
- Credits
- License
Install the package via npm:
npm install @nestbolt/settingsOr via yarn:
yarn add @nestbolt/settingsOr via pnpm:
pnpm add @nestbolt/settingsThis package requires the following peer dependencies, which you likely already have in a NestJS project:
@nestjs/common ^10.0.0 || ^11.0.0
@nestjs/core ^10.0.0 || ^11.0.0
@nestjs/typeorm ^10.0.0 || ^11.0.0
typeorm ^0.3.0
reflect-metadata ^0.1.13 || ^0.2.0
Optional:
@nestjs/event-emitter ^2.0.0 || ^3.0.0
- Register the module in your
AppModule:
import { SettingsModule } from "@nestbolt/settings";
@Module({
imports: [
TypeOrmModule.forRoot({
/* ... */
}),
SettingsModule.forRoot({
defaults: [
{ key: "app.name", value: "MyApp", type: "string" },
{ key: "app.perPage", value: 25, type: "number" },
],
}),
],
})
export class AppModule {}- Inject and use the service:
import { SettingsService } from "@nestbolt/settings";
@Injectable()
export class AppService {
constructor(private readonly settings: SettingsService) {}
async getAppName(): Promise<string> {
return this.settings.get<string>("app.name", "Default");
}
async updateTheme(theme: string): Promise<void> {
await this.settings.set("app.theme", theme, { group: "appearance" });
}
}SettingsModule.forRoot({
cacheTtl: 60000, // 1 minute cache (default)
autoSeed: true, // Auto-seed defaults (default: true)
defaults: [
{ key: "app.name", value: "MyApp", type: "string", group: "app" },
{ key: "app.debug", value: false, type: "boolean", group: "app" },
{
key: "mail.from",
value: "noreply@example.com",
type: "string",
group: "mail",
},
],
});SettingsModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
cacheTtl: config.get("SETTINGS_CACHE_TTL", 60000),
defaults: [
{ key: "app.name", value: config.get("APP_NAME"), type: "string" },
],
}),
});The module is registered as global — SettingsService is available everywhere without re-importing.
Inject SettingsService into any service or controller:
| Method | Returns | Description |
|---|---|---|
get<T>(key, default?) |
Promise<T> |
Get a setting value, returns default if not found |
getOrFail<T>(key) |
Promise<T> |
Get a setting value, throws SettingNotFoundException if not found |
set(key, value, options?) |
Promise<SettingEntity> |
Create or update a setting |
has(key) |
Promise<boolean> |
Check if a setting exists |
forget(key) |
Promise<void> |
Delete a setting |
all() |
Promise<Record<string, any>> |
Get all settings as a key-value map |
group(name) |
Promise<Record<string, any>> |
Get all settings in a group |
flushCache() |
void |
Clear the in-memory cache |
Settings support four types with automatic casting:
| Type | Stored As | Cast To |
|---|---|---|
string |
text | string |
number |
text | Number() |
boolean |
text | true/"1" = true, else false |
json |
text | JSON.parse() |
await settings.set("app.port", 3000, { type: "number" });
await settings.set("app.debug", true, { type: "boolean" });
await settings.set("app.config", { theme: "dark" }, { type: "json" });If no type is specified, it is inferred from the value.
Organize settings by group for easy retrieval:
await settings.set("mail.host", "smtp.example.com", { group: "mail" });
await settings.set("mail.port", "587", { group: "mail" });
const mailSettings = await settings.group("mail");
// { 'mail.host': 'smtp.example.com', 'mail.port': '587' }Settings are cached in memory with a configurable TTL (default: 60 seconds). Set cacheTtl: 0 to disable caching.
The cache is automatically invalidated when you call set() or forget(). Use flushCache() to manually clear it.
When autoSeed is enabled (default), the module seeds any defaults that don't already exist in the database during initialization. Existing settings are never overwritten.
When @nestjs/event-emitter is installed, the following events are emitted:
| Event | Payload |
|---|---|
settings.created |
{ key, value, type, group } |
settings.updated |
{ key, oldValue, newValue, type } |
settings.deleted |
{ key, lastValue } |
| Option | Type | Default | Description |
|---|---|---|---|
defaults |
SettingDefinition[] |
undefined |
Default settings to seed on init |
cacheTtl |
number |
60000 |
Cache TTL in ms (0 to disable) |
autoSeed |
boolean |
true |
Auto-seed defaults if keys don't exist |
npm testRun tests in watch mode:
npm run test:watchGenerate coverage report:
npm run test:covPlease see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
If you discover any security-related issues, please report them via GitHub Issues with the security label instead of using the public issue tracker.
The MIT License (MIT). Please see License File for more information.