NestboltNestbolt

@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, or json and read back the right runtime type. The type is either inferred from the value or set explicitly via service.set(key, value, { type }).
  • In-memory cache with TTL -- get-by-key reads are cached (default 60_000ms); the cache invalidates automatically on set() and forget(). Set cacheTtl: 0 to disable.
  • Race-safe writes -- set() runs inside dataSource.transaction(...) and uses repository.upsert() so two concurrent setters on the same key produce a single row, not a UNIQUE violation.
  • Auto-seeding -- pass defaults to forRoot() and the module fills in any missing keys on init using INSERT ... 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 a SETTING_<key> provider for each entry in defaults.
  • Group filtering -- tag settings with a group and call service.group("mail") to fetch a whole subtree as a Record<string, unknown>.
  • Optional event emission -- emits settings.created, settings.updated, and settings.deleted via @nestjs/event-emitter when 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 SettingsService API: get, set, has, forget, all, group, and the cache.
  • Seeding -- pre-populate keys at startup with defaults.
  • Events -- listen to settings.created, settings.updated, and settings.deleted.