@nestbolt/settings
Service
The full SettingsService API -- read, write, list, delete, and manage the cache.
SettingsService is the primary API surface. After registering SettingsModule.forRoot() (or forRootAsync()), inject it anywhere:
import { Injectable } from "@nestjs/common";
import { SettingsService } from "@nestbolt/settings";
@Injectable()
export class MyService {
constructor(private readonly settings: SettingsService) {}
}Every public method validates the key argument up front -- non-empty string, ≤255 chars, no control characters. Bad inputs throw before any DB work happens.
Read
get(key, defaultValue?)
Returns the value associated with key, or defaultValue if the key is missing, or undefined if neither is set:
const name = await this.settings.get<string>("app.name");
const port = await this.settings.get<number>("app.port", 3000);
const missing = await this.settings.get("nope"); // undefinedThe runtime type is reconstructed from the stored type column:
| Stored type | Returned shape |
|---|---|
"string" | the raw string |
"number" | Number(raw) |
"boolean" | raw === "true" || raw === "1" |
"json" | JSON.parse(raw) (falls back to the raw string if parsing fails) |
Reads are served from an in-memory cache for cacheTtl ms (default 60_000).
getOrFail(key)
Same as get(), but throws SettingNotFoundException when the key is missing:
import { SettingNotFoundException } from "@nestbolt/settings";
try {
const apiKey = await this.settings.getOrFail<string>("api.key");
} catch (err) {
if (err instanceof SettingNotFoundException) { /* ... */ }
}Use this for settings that must exist for the application to function -- it surfaces missing-key bugs at the call site instead of letting undefined propagate.
has(key)
Returns true if the key exists (in cache or DB):
if (await this.settings.has("app.maintenance")) { /* ... */ }has() short-circuits via the cache when the key has been read recently.
Write
set(key, value, options?)
Inserts or updates the setting. The type is inferred from value when options.type is omitted:
await this.settings.set("app.name", "Nestbolt"); // type: "string"
await this.settings.set("app.port", 3000); // type: "number"
await this.settings.set("app.debug", true); // type: "boolean"
await this.settings.set("mail", { host: "smtp" }); // type: "json"
await this.settings.set("kept-as-string", "42", { type: "string" }); // explicit overrideOptions
| Field | Type | Description |
|---|---|---|
type | "string" | "number" | "boolean" | "json" | Explicit type; overrides inference. |
group | string | Tag for filtering with service.group(name). |
description | string | Human-readable description. |
set() runs inside dataSource.transaction(...) and uses repository.upsert(payload, ['key']) followed by a findOneOrFail() to fetch the saved row, so:
- Concurrent setters on the same key collapse into a single row -- no UNIQUE violations.
- The returned entity reflects the actual DB state after the upsert (including DB-managed fields like timestamps).
- The local cache is updated to match.
When the key is new, settings.created is emitted; when it already existed, settings.updated is emitted with both oldValue and newValue.
forget(key)
Deletes the setting. No-ops if the key doesn't exist:
await this.settings.forget("app.maintenance");Runs inside a transaction. Emits settings.deleted with the last value -- but only if the key actually existed.
Bulk Read
all()
Returns every setting as a single key-value map:
const everything = await this.settings.all();
// { "app.name": "Nestbolt", "app.port": 3000, ... }Each value is reconstructed from its stored type. This always hits the database.
group(name)
Returns settings tagged with group: name:
const mailConfig = await this.settings.group("mail");
// { "mail.host": "smtp.example.com", "mail.port": 587 }The group column is indexed, so this scales to large stores.
Cache
flushCache()
Clears the in-memory cache without touching the database:
this.settings.flushCache();Useful when an out-of-band write (e.g., a SQL migration) has changed the underlying table and you want subsequent reads to re-fetch.
Cache Behavior
cacheTtl > 0: every successfulget()/getOrFail()caches the value forcacheTtlms.cacheTtl === 0: caching is disabled at every level.get()always hits the DB;set()andforget()never write to the cache.set(key, ...)overwrites the cache entry with the new value.forget(key)invalidates the cache entry.has(key)returnstruestraight from the cache when an entry is still fresh.
The cache is process-local. For multi-process deployments where one process writes and another reads, set cacheTtl: 0 or hook the cross-process invalidation into the event system.
Module Options
getOptions()
Returns the resolved SettingsModuleOptions passed to forRoot() / forRootAsync():
const options = this.settings.getOptions();
console.log(options.cacheTtl);Static: SettingsService.getInstance()
Returns the active SettingsService instance (the most recently initialized one), or null if the module hasn't been initialized:
import { SettingsService } from "@nestbolt/settings";
const service = SettingsService.getInstance();You normally don't need this -- prefer dependency injection. It's exposed for cases where the call site can't have the service injected (e.g., entity-side helpers, plain functions called outside the request lifecycle).
The instance is set in onModuleInit() and cleared in onModuleDestroy().