Configuration
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)#
| # | Source | Typical use |
|---|---|---|
| 1 | backend/config/appsettings.json | Defaults for every environment (logging, ports, auth issuer). |
| 2 | backend/config/appsettings.<NODE_ENV>.json | Per-environment overrides — the template ships appsettings.production.json. |
| 3 | backend/config/environments/env.<NODE_ENV>.json | Database settings for MasterRecord (keyed by AppContext). |
| 4 | User secrets — ~/.master/usersecrets/<id>/secrets.json | Development secrets via master secrets set. |
| 5 | Environment variables — Section__Key | Production values (Auth__JwtSecret, Logging__LogLevel__Default). |
| 6 | Command line — --Section:Key=value | One-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.
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 flagsappsettings.json#
The template’s defaults:
{
"Logging": {
"LogLevel": { "Default": "info", "Request": "info" },
"Console": { "Json": false }
},
"Server": { "Port": 3001 },
"Frontend": { "Url": "http://localhost:3000" },
"Auth": { "Issuer": "app", "ExpiresIn": "1h" }
}{
"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.
{ "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 overrides —
Section__Keynames 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 forAuth:JwtSecret) andNEXT_PUBLIC_API_URLfor the frontend.
Copy .env.example to .env per environment:
# 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-valuemaster.config.js#
Drives the CLI’s dev/build/start orchestration — not the backend’s runtime configuration.
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#
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 });
}
}Section:Key names, code reads one key — Auth:JwtSecret — and the environment decides where the value comes from.