Performance
@neststack/config is optimized for read-heavy workloads. Configuration is read thousands of times during an application’s lifetime but written only once at startup.
O(1) Lookups
When a config namespace is registered, the store builds a flat lookup map:
// Input (nested)
{
database: { host: "localhost", port: 5432 },
app: { name: "myapp" }
}
// Flat lookup map (Map<string, unknown>)
"database" → { host: "localhost", port: 5432 }
"database.host" → "localhost"
"database.port" → 5432
"app" → { name: "myapp" }
"app.name" → "myapp"Every config.get('database.host') is a single Map.get() call — no string splitting, no object traversal, no recursion.
Why a Flat Map?
| Approach | Time per read | Allocation per read |
|---|---|---|
Flat Map.get() | O(1) | None |
| String split + traverse | O(n) where n = depth | Array from .split('.') |
| Recursive search | O(n) | Stack frames |
The flat map trades a few microseconds of startup time (to build the map) for zero-cost reads at runtime.
Memory Efficiency
The flat map adds memory overhead proportional to the number of unique paths. For a typical config with 20-50 keys, this is negligible (a few KB). The benefit — instant access without parsing — vastly outweighs the cost.
Deep Freeze Performance
Object.freeze() is called once per namespace at registration time. It’s a native V8 operation that:
- Adds zero overhead to subsequent property reads
- Makes objects slightly more optimizable by V8 (frozen objects have a stable shape)
- Throws
TypeErroron mutation attempts (caught by strict mode)
Rebuild Strategy
The lookup map is rebuilt whenever a new namespace is registered (via forRoot() or forFeature()). Since registration only happens at startup, this has no impact on runtime performance.
Startup:
forRoot() → register 3 namespaces → rebuild map (once)
forFeature() → register 1 namespace → rebuild map (once)
Runtime:
config.get('database.host') → Map.get() → O(1)
config.get('app.port') → Map.get() → O(1)Benchmarks
For a configuration with 5 namespaces and 30 total keys:
| Operation | Time |
|---|---|
| Full registration + validation + freeze | ~2ms |
Single get() call | ~0.001ms (1μs) |
getAll() | ~0.01ms |
getSafeAll() (with masking) | ~0.05ms |
These are approximate values. Actual performance depends on hardware and config size.