Skip to content

Getting Started

Configuard turns a flat list of configuration rows — typically the contents of a config database table — into a nested, typed, immutable object your runtime can read safely.

Terminal window
npm i configuard

notation is a runtime dependency and is installed with it.

Each row is an IConfigItem — a key in dot/bracket notation, a raw string value, and the metadata that says how to parse it and who may see it. Here is a small device config used throughout these docs:

config rows
[
{
"accessor": "system",
"appAccess": null,
"key": "@UIColors",
"type": "string",
"listType": "csl",
"value": "Blue,Red,Green,Amber",
"options": null,
"defaultValue": null,
"editable": false,
"requiresReboot": false,
"encrypt": false,
"description": "Reusable list of allowed UI colors."
},
{
"accessor": "system",
"appAccess": null,
"key": "company.name",
"type": "string",
"listType": "none",
"value": "Acme",
"options": null,
"defaultValue": null,
"editable": true,
"requiresReboot": false,
"encrypt": false,
"description": "Display name of the operator."
},
{
"accessor": "system",
"appAccess": null,
"key": "device.port",
"type": "integer",
"listType": "none",
"value": "8080",
"options": null,
"defaultValue": "8080",
"editable": false,
"requiresReboot": true,
"encrypt": false,
"description": "Port the device listens on. Reboot to apply."
},
{
"accessor": "system",
"appAccess": null,
"key": "device.config.updateOnBoot",
"type": "boolean",
"listType": "none",
"value": "true",
"options": null,
"defaultValue": "true",
"editable": true,
"requiresReboot": false,
"encrypt": false,
"description": "Whether the device refreshes its config on boot."
},
{
"accessor": "system",
"appAccess": null,
"key": "device.config.lifeSpan",
"type": "integer",
"listType": "none",
"value": "1440",
"options": null,
"defaultValue": "1440",
"editable": true,
"requiresReboot": false,
"encrypt": false,
"description": "Config validity window, in minutes."
},
{
"accessor": "system",
"appAccess": null,
"key": "device.ui.accent",
"type": "string",
"listType": "none",
"value": "Amber",
"options": "${@UIColors}",
"defaultValue": "Blue",
"editable": true,
"requiresReboot": false,
"encrypt": false,
"description": "Accent color, constrained to the @UIColors list."
},
{
"accessor": "system",
"appAccess": null,
"key": "device.protocol.transports",
"type": "string",
"listType": "array",
"value": "http,mqtt",
"options": null,
"defaultValue": null,
"editable": true,
"requiresReboot": true,
"encrypt": false,
"description": "Enabled transports, parsed into a string array."
},
{
"accessor": "system",
"appAccess": null,
"key": "environment.host",
"type": "string",
"listType": "none",
"value": "device.local",
"options": null,
"defaultValue": null,
"editable": true,
"requiresReboot": false,
"encrypt": false,
"description": "Base hostname for device endpoints."
},
{
"accessor": "system",
"appAccess": null,
"key": "device.diagnostics.uploadUrl",
"type": "string",
"listType": "none",
"value": "https://${environment.host}/upload",
"options": null,
"defaultValue": null,
"editable": true,
"requiresReboot": false,
"encrypt": false,
"description": "Diagnostics endpoint, templated off environment.host."
},
{
"accessor": "system",
"appAccess": null,
"key": "db.password",
"type": "string",
"listType": "none",
"value": "enc:5f3a9c2e7b104d8f",
"options": null,
"defaultValue": null,
"editable": true,
"requiresReboot": false,
"encrypt": true,
"description": "Database password, stored encrypted at rest."
}
]

Pass the rows to the constructor with the accessor doing the reading. The result is built once, deep-frozen, and read by notation path:

import { Configuard, AccessorType } from 'configuard';
const cfg = new Configuard(rows, { accessor: AccessorType.SYSTEM });
cfg.data;
// {
// company: { name: 'Acme' },
// device: {
// port: 8080,
// config: { updateOnBoot: true, lifeSpan: 1440 },
// ui: { accent: 'Amber' },
// protocol: { transports: ['http', 'mqtt'] },
// diagnostics: { uploadUrl: 'https://device.local/upload' }
// },
// environment: { host: 'device.local' },
// db: { password: 'enc:5f3a9c2e7b104d8f' } // encrypted — no decrypt hook here
// }
cfg.get<number>('device.port'); // 8080 — a number, not "8080"
cfg.get('device.protocol.transports'); // ['http', 'mqtt']
cfg.has('device.ui.accent'); // true

Note what the build did for you: strings were cast to their declared type ("8080"8080), the array row was split into a list, the ${environment.host} template was resolved, and the whole object was frozen so nothing downstream can mutate live config.

Configuard sits across the whole life of a setting, not just the read:

  1. Store — keep settings as rows in one config table.
  2. Build & usenew Configuard(rows) produces the nested, type-cast, ABAC-filtered object for your runtime. Read it via .data / .get() / .has().
  3. Edit in a UIConfiguard.parseFlat() returns the same flat list with templates resolved and option lists expanded, ready to render as form fields.
  4. Save backConfiguard.serializeFlat() validates the edits, serializes them to DB strings, and returns the diff to persist.

Configuard also fails loud: a corrupt row throws at construction rather than silently producing a partial object.