Master Guides

Configuration

ASP.NET Core-style layered configuration — in a handful of predictable files.

A scaffolded backend reads its settings the way ASP.NET Core does: appsettings.json is overlaid by an environment-specific file, then by your database config, then by user secrets, then by environment variables and command-line flags. Ports and the frontend toggle for the CLI live separately in master.config.js.

Precedence (later wins)#

#SourceTypical use
1backend/config/appsettings.jsonDefaults for every environment (logging, ports, auth issuer).
2backend/config/appsettings.<NODE_ENV>.jsonPer-environment overrides — the template ships appsettings.production.json.
3backend/config/environments/env.<NODE_ENV>.jsonDatabase settings for MasterRecord (keyed by AppContext).
4User secrets — ~/.master/usersecrets/<id>/secrets.jsonDevelopment secrets via master secrets set.
5Environment variables — Section__KeyProduction values (Auth__JwtSecret, Logging__LogLevel__Default).
6Command line — --Section:Key=valueOne-off overrides when starting node server.js.

Keys are case-insensitive and :-separated; __ in an environment variable maps to :. Values from env/argv are coerced ("true"true, "42"42).

NODE_ENV#

NODE_ENV selects the appsettings.<env>.json and env.<env>.json layers for both MasterController and MasterRecord. server.js defaults it to development; master dev sets development, master start sets production, and master test sets test. master db --env <env> picks the environment for migrations.

backend/server.js (excerpt)
process.env.NODE_ENV ??= 'development';
master.root = __dirname;
master.environmentType = process.env.NODE_ENV;
master.userSecretsId = pkg.name;            // enables the user-secrets layer

const server = master.setupServer('http'); // builds master.configuration from the layers above
const config = master.configuration;

config.get('Auth:Issuer');                 // string
config.get('Server:Port', 3001);           // with default
master.environment.isProduction;           // IHostEnvironment-style flags

appsettings.json#

The template’s defaults:

backend/config/appsettings.json
{
  "Logging": {
    "LogLevel": { "Default": "info", "Request": "info" },
    "Console": { "Json": false }
  },
  "Server": { "Port": 3001 },
  "Frontend": { "Url": "http://localhost:3000" },
  "Auth": { "Issuer": "app", "ExpiresIn": "1h" }
}
backend/config/appsettings.production.json
{
  "Logging": {
    "LogLevel": { "Default": "warn", "Request": "warn" },
    "Console": { "Json": true }
  }
}

server.js reads Server:Port (unless PORT is set), Frontend:Url for CORS (unless FRONTEND_URL is set) and Auth:* for the optional JWT scheme. Add your own sections freely and read them with this.configuration.get('Smtp:Host') in a controller, or bind and validate them with the options pattern — see MasterController Configuration & Options.

Database settings#

Per environment, in backend/config/environments/env.<env>.json, keyed by the context name. master new writes development, test and production files for the database you chose. See Connecting a Database.

env.development.json / env.test.json
{ "AppContext": { "type": "better-sqlite3", "connection": "db/" } }
{ "AppContext": { "type": "better-sqlite3", "connection": "db/test/" } }

Secrets#

Never put secrets in appsettings*.json. In development run master secrets set Auth:JwtSecret <value> — the value is stored outside the repo and read automatically because server.js sets master.userSecretsId. In production set the same key as an environment variable. Full guide: Secrets.

Environment variables#

Two kinds are in play:

  • Configuration overridesSection__Key names that land on the configuration keys above: Auth__JwtSecret, Server__Port, Logging__LogLevel__Default.
  • Process variables read directly by server.js / Next.js — NODE_ENV, PORT, FRONTEND_URL, JWT_SECRET (an alias for Auth:JwtSecret) and NEXT_PUBLIC_API_URL for the frontend.

Copy .env.example to .env per environment:

.env.example
# Where the frontend reaches the backend API (used by Next.js fetches).
NEXT_PUBLIC_API_URL=http://localhost:3001

# Where the backend allows CORS requests from (the frontend origin).
FRONTEND_URL=http://localhost:3000

# Backend configuration can also come from env vars using Section__Key
# (same keys as backend/config/appsettings.json), e.g.:
#   Logging__LogLevel__Default=debug
#   Server__Port=3001
# Secrets: in development use `master secrets set Auth:JwtSecret <32+ chars>`;
# in production set the env var:
#   Auth__JwtSecret=change-me-to-a-long-random-secret-value

master.config.js#

Drives the CLI’s dev/build/start orchestration — not the backend’s runtime configuration.

master.config.js
export default {
  frontend: true,        // is there a Next.js frontend?
  backendPort: 3001,
  frontendPort: 3000,
  backendDir: 'backend',
  frontendDir: 'frontend',
};

master dev/start pass PORT=<backendPort> to the backend and PORT=<frontendPort> + NEXT_PUBLIC_API_URL to the frontend.

Reading configuration in code#

backend/app/controllers/mailController.js
export default class MailController {
  constructor(requestObject) { this.requestObject = requestObject; }

  async send() {
    const host = this.configuration.get('Smtp:Host');      // any layer
    const env = this.environment.name;                      // 'development' | 'test' | 'production'
    this.logger.info('sending via {host} in {env}', { host, env });
    this.ok({ sent: true });
  }
}
Same keys everywhere
Because user secrets, JSON files and environment variables all map onto the same Section:Key names, code reads one key — Auth:JwtSecret — and the environment decides where the value comes from.