Environment variables are indispensable for configuring applications without hard‑coding sensitive or environment‑specific values.
When working with Node.js and npm scripts, you have several strategies for passing environment variables to your scripts.
In this post, we’ll explore three key approaches:
-
Using the
config
key inpackage.json
to store and access custom settings. -
Using the
cross-env
package to set environment variables in a cross‑platform way. -
Accessing those settings inside your Node.js application via
process.env
.
Along the way, we’ll also see how tools like Commitizen leverage the same config
key to read values such as the driver
for your commit conventions.
Storing Custom Settings with npm config
in package.json
npm supports a config
section inside package.json
, which lets you declare custom configuration keys and default values.
Scripts can access these via npm_package_config_<key>
environment variables.
// package.json
{
"name": "my-app",
"version": "1.0.0",
"config": {
"apiHost": "https://api.example.com",
"port": "3000"
},
"scripts": {
"start": "node server.js",
"report": "echo API is at $npm_package_config_apiHost on port $npm_package_config_port"
}
}
When you run:
npm run report
you’ll see:
API is at https://api.example.com on port 3000
Overriding Config at Runtime
You can override those defaults when invoking npm scripts:
npm run report --apiHost=https://api.staging.local --port=4000
Under the hood, npm maps those flags to the same environment variables (npm_package_config_apiHost
, npm_package_config_port
).
Setting Environment Variables with cross-env
Unix shells let you write NODE_ENV=production webpack
, but Windows CMD and PowerShell use different syntax. To unify across platforms, use cross-env
:
npm install --save-dev cross-env
Then in your scripts:
"scripts": {
"build": "cross-env NODE_ENV=production webpack --config webpack.prod.js",
"dev": "cross-env API_URL=$npm_package_config_apiHost PORT=$npm_package_config_port node server.js"
}
Now npm run build
and npm run dev
work identically on Windows, macOS, and Linux—no syntax changes needed.
Example: Commitizen and the config
Key
Commitizen is a tool for standardized commit messages. It reads its adapter (driver
) from the config.commitizen.path
value in package.json
:
// package.json
{
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"scripts": {
"commit": "git-cz"
}
}
When you run npm run commit
(alias for git-cz
), Commitizen looks up config.commitizen.path
and uses that adapter to guide your commit prompts.
Accessing config
Vars Inside Your Node.js App
Once npm has exposed your config
values as environment variables, you can read them in any Node.js file using process.env
:
// server.js
const express = require('express');
const app = express();
// npm_package_config_port comes from config.port in package.json
const PORT = process.env.npm_package_config_port || 3000;
const API_URL = process.env.npm_package_config_apiHost || 'http://localhost:4000';
app.get('/', (req, res) => {
res.send(`API is at ${API_URL}`);
});
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
});
- If you run
npm run dev
, npm will injectnpm_package_config_port
andnpm_package_config_apiHost
for you. - If someone runs
node server.js
directly, the fallback (|| 3000
) ensures your app still starts with a sensible default. - You can still override at runtime:
npm run dev --npm_config_port=5000
will setprocess.env.npm_package_config_port === '5000'
.
5. Best Practices
-
Centralize defaults under
config
inpackage.json
so scripts and code share the same source of truth. -
Use
cross-env
for any script that sets real OS environment variables to guarantee cross‑platform compatibility. -
Document overrides: show teammates how to tweak via
--npm_config_<key>
. -
Leverage in tooling: Commitizen, linters, deploy scripts, etc., all honor the same
config
block.
By combining npm’s config
key, cross-env
, and simple process.env
access, you get a flexible, DRY, and cross‑platform approach to managing configuration in your Node.js projects.
If you found this helpful, feel free to share
Let’s connect!!: 🤝
Top comments (1)
pretty cool, configs always get messy for me and this way actually makes them so much easier to manage
you ever run into issues syncing config between local and CI environments