@nestbolt/settings
Introduction
Database-backed key-value application settings for NestJS with TypeORM -- typed access, in-memory caching, transactional writes, and auto-seeding.
@nestbolt/settings is a database-backed key-value settings store for NestJS with TypeORM. Read and write configuration values that need to persist across deploys (feature flags, mail server hosts, app metadata, anything you'd otherwise stuff into env vars but want editable at runtime), get typed access via string | number | boolean | json, and serve hot reads from an in-memory cache so the database isn't hit on every request. Writes are transactional and use INSERT ... ON CONFLICT DO UPDATE semantics so two concurrent setters on the same key won't tear.
Key Features
- Typed values -- store
string,number,boolean, orjsonand read back the right runtime type. The type is either inferred from the value or set explicitly viaservice.set(key, value, { type }). - In-memory cache with TTL -- get-by-key reads are cached (default
60_000ms); the cache invalidates automatically onset()andforget(). SetcacheTtl: 0to disable. - Race-safe writes --
set()runs insidedataSource.transaction(...)and usesrepository.upsert()so two concurrent setters on the same key produce a single row, not aUNIQUEviolation. - Auto-seeding -- pass
defaultstoforRoot()and the module fills in any missing keys on init usingINSERT ... OR IGNORE, so re-deploys never overwrite values an operator changed. @Setting(key)parameter decorator -- inject the resolved value of a defaulted key directly into a constructor; the module creates aSETTING_<key>provider for each entry indefaults.- Group filtering -- tag settings with a
groupand callservice.group("mail")to fetch a whole subtree as aRecord<string, unknown>. - Optional event emission -- emits
settings.created,settings.updated, andsettings.deletedvia@nestjs/event-emitterwhen the package is installed; no-op otherwise. - Key validation -- keys are checked at every public entry point: must be a non-empty string, ≤255 chars, no control characters. Catches typos and bad inputs before they reach the database.
Architecture
The package is built around several core components:
SettingsModule registers all providers globally (or scoped, when you set global: false). You call SettingsModule.forRoot() or SettingsModule.forRootAsync() once in your root module, and SettingsService becomes available everywhere via dependency injection.
SettingsService is the primary API surface: get(), getOrFail(), set(), has(), forget(), all(), group(), flushCache(). Mutations run inside dataSource.transaction(...) and use repository.upsert() for race-safe writes. Reads go through an in-memory cache when cacheTtl > 0.
SettingEntity is the TypeORM entity backing the store -- a single table with key (unique), value (text), type (string | number | boolean | json), group (indexed, nullable), description (nullable), and timestamps. Add it to your TypeORM entities array.
@Setting(key) decorator is a parameter decorator that resolves to a SETTING_<key> provider created by forRoot() for each entry in defaults. Inject a setting directly into a constructor without going through the service.
Basic Usage
import { SettingsModule, SettingsService } from "@nestbolt/settings";
@Module({
imports: [
TypeOrmModule.forRoot({ /* ... */ }),
SettingsModule.forRoot({
defaults: [
{ key: "app.name", value: "Nestbolt", type: "string" },
{ key: "mail.host", value: "smtp.example.com", group: "mail" },
{ key: "mail.port", value: 587, type: "number", group: "mail" },
],
}),
],
})
export class AppModule {}
@Injectable()
export class MyService {
constructor(private readonly settings: SettingsService) {}
async setMaintenanceMode(on: boolean) {
await this.settings.set("app.maintenance", on, { type: "boolean" });
}
async getMailConfig() {
return this.settings.group("mail"); // { "mail.host": "...", "mail.port": 587 }
}
}Next Steps
- Installation -- install the package and its peer dependencies.
- Quick Start -- set up the module, write your first setting, and read it back in minutes.
- Configuration --
forRoot,forRootAsync, and module-level options. - Decorator -- inject defaulted settings directly with
@Setting(key). - Service -- the full
SettingsServiceAPI:get,set,has,forget,all,group, and the cache. - Seeding -- pre-populate keys at startup with
defaults. - Events -- listen to
settings.created,settings.updated, andsettings.deleted.