The config file for Node.js you always wanted

colinmcd1 pts0 comments

nub.jsonc — The config file for Node.js you always wanted — Nub

← BlogTable of Contents

The node CLI doesn't try too hard to be ergonomic. It's designed like a util, with lots of explicit flags and environment variables. Even as new features like "node:test" and watch mode have landed, Node has studiously avoided adding any subcommands (e.g. node test or node watch), favoring --test and --watch instead. It's not uncommon to see large node commands like this in package.jsons or other scripts.

start.sh

node --import ./instrumentation.mjs \<br>--env-file=.env --env-file=.env.local \<br>--enable-source-maps \<br>--conditions=production \<br>--unhandled-rejections=strict \<br>--trace-warnings \<br>--max-old-space-size=8192 \<br>--stack-trace-limit=50 \<br>src/server.ts

Over the years many have asked for Node.js to add support for a project-level config file that could alleviate some of the flag madness.

nodejs/node#53787

nodejs/node#43895

nodejs/loaders#98

As a matter of fact, Node quietly shipped a config file in Node v22.16 last year (node.config.json). But it's disabled by default and (rather antithetically) gated behind a scary-looking flag.

node --experimental-default-config-file index.ts

This is the kind of ergonomic issue in Node.js that Nub is perfectly equipped to solve.

Nub is a Rust toolkit for Node.js. The nub command is flag-for-flag compatible with node, while adding full support for TypeScript, JSX, tsconfig.json, .env loading, and modern Web and ECMAScript APIs. It also includes a fast script runner (nub run), package runner (nubx), Node version manager (nub node), and pnpm-compatible package manager.

nub index.ts # supports TypeScript, JSX, Worker, latest ECMAScript syntax<br>nub run dev # run package.json scripts<br>nub install # install using your existing lockfile<br>nubx prisma generate # run package CLIs<br>nub node install 26 # pin and provision Node

Introducing nub.jsonc

In v0.7, Nub introduced its config file nub.jsonc. It's the config file for Node.js you've always wanted, with the current runtime fields in one place:

"$schema": "https://nubjs.com/schema/latest.json",

// Nub configs<br>"envFile": [".env", ".env.local"], // disable with false<br>"loader": { ".graphql": "text" }, // text|jsonc|json5|toml|yaml|ts|tsx|jsx<br>"conditions": ["development"], // additional export conditions to respect<br>"tsconfig": "./tsconfig.runtime.json", // JSX, decorators, paths/baseURL<br>"verifyDeps": "error", // check node_modules freshness before runs<br>"preload": [ // for telemetry, hardening, etc.<br>"./instrumentation.ts",<br>"dd-trace/initialize.mjs"<br>],<br>"nodeCompat": false, // disables Nub augmentations

// Node.js/v8 configs<br>"v8Flags": [<br>"--stack-size=2000",<br>"--prof",<br>"--allow-natives-syntax",<br>"--trace-deopt"<br>], // not supported in node.config.json<br>"nodeOptions": [ // passed as NODE_OPTIONS<br>"--stack-trace-limit=50",<br>"--max-old-space-size=8192",<br>"--frozen-intrinsics"

Custom environment loading

By default Nub loads these files, from lowest to highest precedence:

.env

.env.${APP_ENV}

.env.local

.env.${APP_ENV}.local

Override discovery with one file:

nub.jsonc

"envFile": ".env.local"

Load several files in order:

nub.jsonc

"envFile": [".env", ".env.production", ".env.local"]

Paths support environment-variable expansion:

nub.jsonc

"envFile": ".env.${NODE_ENV}"

NODE_ENV=production nub index.ts # reads .env.production

Disable environment loading entirely:

nub.jsonc

"envFile": false

Varlock<br>As of Nub v0.7, Nub has first-party Varlock support. If a project has a .env.schema and Varlock is installed, Nub hands environment loading to Varlock automatically.

Register custom loaders

Map an extension onto one of Nub's built-in loaders and that file type becomes directly importable:

LoaderTreats the file astextUTF-8 textjsoncJSON with comments and trailing commasjson5JSON5tomlTOMLyamlYAMLtsTypeScripttsxTypeScript with JSXjsxJavaScript with JSX

nub.jsonc

"loader": {<br>".graphql": "text",<br>".rules": "yaml"

schema.ts

import schema from "./schema.graphql"; // string<br>import rules from "./access.rules"; // parsed YAML

Increase memory allotment

Quite possibly the single most useful feature of nub.jsonc.

nub.jsonc

"nodeOptions": ["--max-old-space-size=8192"]

Configure telemetry

Telemetry has to initialize before application dependencies, so put it in a project preload instead of every entry point:

nub.jsonc

"preload": [<br>"./instrument.ts",<br>"@opentelemetry/auto-instrumentations-node/register",<br>"dd-trace/initialize.mjs",<br>"@sentry/node/preload"

Harden your environment

Node can freeze selected built-ins and reject access to the legacy Object.prototype.__proto__ accessor before application code runs:

nub.jsonc

"nodeOptions": [<br>"--frozen-intrinsics",<br>"--disable-proto=throw"

Clean up stack traces

Preloads can also establish process-wide behavior that application modules should not have to initialize themselves. This formatter removes dependency frames from V8 stack traces before the entry point runs, including in scripts, tests, and...

node jsonc file config environment local

Related Articles