1520

The scripts portion of my package.json currently looks like this:

"scripts": {
    "start": "node ./script.js server"
}

...which means I can run npm start to start the server. So far so good.

However, I would like to be able to run something like npm start 8080 and have the argument(s) passed to script.js (e.g. npm start 8080 => node ./script.js server 8080). Is this possible?

25 Answers 25

2049

npm 2 and newer

It's possible to pass args to npm run since npm 2 (2014). The syntax is as follows:

npm run <command> [-- <args>]

Note the -- separator, used to separate the params passed to npm command itself, and the params passed to your script. (This is a common convention used by various command line tools).

With the example package.json:

"scripts": {
    "grunt": "grunt",
    "server": "node server.js"
}

here's how to pass the params to those scripts:

npm run grunt -- task:target  // invokes `grunt task:target`
npm run server -- --port=1337 // invokes `node server.js --port=1337`

Note: If your param does not start with - or --, then having an explicit -- separator is not needed; but it's better to do it anyway for clarity.

npm run grunt task:target     // invokes `grunt task:target`

Note below the difference in behavior (test.js has console.log(process.argv)): the params which start with - or -- are passed to npm and not to the script, and are silently swallowed there.

$ npm run test foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js', 'foobar']

$ npm run test -foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js']

$ npm run test --foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js']

$ npm run test -- foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js', 'foobar']

$ npm run test -- -foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js', '-foobar']

$ npm run test -- --foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js', '--foobar']

The difference is clearer when you use a param actually used by npm:

$ npm test --help      // this is disguised `npm --help test`
npm test [-- <args>]

aliases: tst, t

To get the parameter value, see this question. For reading named parameters, it's probably best to use a parsing library like yargs or minimist; nodejs exposes process.argv globally, containing command line parameter values, but this is a low-level API (whitespace-separated array of strings, as provided by the operating system to the node executable).

Sign up to request clarification or add additional context in comments.

20 Comments

This also works perfectly together with packages like yargs; all parameters after the -- can be parsed perfectly in your script.
AFAIKS, this only makes it possible to add parameters to the end of your scripts.. what if you need parameters in the middle?
-- --args holy crap that's weird but okay
@Spock You can use shell functions. Here's an eslint+tslint setup I use to allow passing custom args to eslint, for insance, via "npm run lint -- -f unix": "lint": "f() { eslint -f codeframe $@ . && npm run tslint && echo 'lint clean!'; }; f"
The nicer way to set the "myPackage:myPort 9090" value is with a config flag to the command "--myPackage:myPort=9090" - keithcirkel.co.uk/how-to-use-npm-as-a-build-tool
|
367

You asked to be able to run something like npm start 8080. This is possible without needing to modify script.js or configuration files as follows.

For example, in your "scripts" JSON value, include--

"start": "node ./script.js server $PORT"

And then from the command-line:

$ PORT=8080 npm start

I have confirmed that this works using bash and npm 1.4.23. Note that this work-around does not require GitHub npm issue #3494 to be resolved.

9 Comments

This works really well. You can also do something like node ./script.js server ${PORT:-8080} to make it optional.
I seem to be unable to do this in windows with git bash. Anyone got it working maybe? (the same command works on ubuntu)
Hey @graup this worked for me NODE_PORT=${PORT=8080} (notive the equal) but not the :- syntax
This does not work cross-platform! E.g. on Windows the command would need to be node ./script.js server %PORT%. Consider using cross-var and cross-env.
@JeungminOh ARG1=1 ARG2=2 ARGx=x npm start
|
149

You could also do that:

In package.json:

"scripts": {
    "cool": "./cool.js"
}

In cool.js:

console.log({ myVar: process.env.npm_config_myVar });

In CLI:

npm --myVar=something run-script cool

Should output:

{ myVar: 'something' }

Update: Using npm 3.10.3, it appears that it lowercases the process.env.npm_config_ variables? I'm also using better-npm-run, so I'm not sure if this is vanilla default behavior or not, but this answer is working. Instead of process.env.npm_config_myVar, try process.env.npm_config_myvar

6 Comments

This is wrong. process.env.npm_config_myVar returns true, not the value.
Works with npm version 6.8.0 but only when I used lowercase for the variable name. it looks lilke npm change it to lowercase
Great solution, works with lower case param on npm 6.5.0
@K-ToxicityinSOisgrowing. This is the case if you forgot to add = to parameter. For instance: npm --myVar something ... will return true. But this one: npm --myVar=something ... will return the value itself.
the best answer, thanks. It works also with default scripts npm start --port=5050 and then make const serverPort = process.env.npm_config_port || '8080'
|
130

I had been using this one-liner in the past, and after a bit of time away from Node.js had to try and rediscover it recently. Similar to the solution mentioned by @francoisrv, it utilizes the npm_config_* variables.

Create the following minimal package.json file:

{
  "name": "argument",
  "version": "1.0.0",
  "scripts": {
    "argument": "echo \"The value of --foo is '${npm_config_foo}'\""
  }
}

Run the following command:

npm run argument --foo=bar

Observe the following output:

The value of --foo is 'bar'

All of this is nicely documented in the npm official documentation:

Note: The Environment Variables heading explains that variables inside scripts do behave differently to what is defined in the documentation. This is true when it comes to case sensitivity, as well whether the argument is defined with a space or equals sign.

Note: If you are using an argument with hyphens, these will be replaced with underscores in the corresponding environment variable. For example, npm run example --foo-bar=baz would correspond to ${npm_config_foo_bar}.

Note: For non-WSL Windows users, see @Doctor Blue's comments below... TL;DR replace ${npm_config_foo} with %npm_config_foo%.

11 Comments

Hello. I'm attempting to use your example but I'm afraid it's not working for me. I copy-pasted your "argument" script, and did the same for the command to run (npm run argument --foo=bar), but the variable isn't replaced: "The value of --foo is '${npm_config_foo}'". Running on Windows 10 if that matters, with version 6.9.0 of NPM.
@DoctorBlue Ahh right, Node and Windows don't always play nice... This article might shed some light on environment variables in npm scripts: (TL;DR commands go straight to the host OS, even if launched from another shell) blog.risingstack.com/node-js-windows-10-tutorial/… I'm not sure on your setup, but if you're using Git Bash to run Node, you may want to consider running it through WSL :) learn.microsoft.com/en-us/windows/nodejs/setup-on-wsl2
I figured it out. Just had to use %npm_config_foo% instead. Pure Windows command line / powershell here. (Don't have a choice either.)
You can use cross-env: npmjs.com/package/cross-env
Also, be careful when you are trying to inject "v" or "version" to command - npm thinking, that you are trying to determine it's own version and will output you it ignoring command. But "ver" is acceptable, for example :)
|
111

jakub.g's answer is correct, however an example using grunt seems a bit complex.

So my simpler answer:

- Sending a command line argument to an npm script

Syntax for sending command line arguments to an npm script:

npm run [command] [-- <args>]

Imagine we have an npm start task in our package.json to kick off webpack dev server:

"scripts": {
  "start": "webpack-dev-server --port 5000"
},

We run this from the command line with npm start

Now if we want to pass in a port to the npm script:

"scripts": {
  "start": "webpack-dev-server --port process.env.port || 8080"
},

running this and passing the port e.g. 5000 via command line would be as follows:

npm start --port:5000

- Using package.json config:

As mentioned by jakub.g, you can alternatively set params in the config of your package.json

"config": {
  "myPort": "5000"
}

"scripts": {
  "start": "webpack-dev-server --port process.env.npm_package_config_myPort || 8080"
},

npm start will use the port specified in your config, or alternatively you can override it

npm config set myPackage:myPort 3000

- Setting a param in your npm script

An example of reading a variable set in your npm script. In this example NODE_ENV

"scripts": {
  "start:prod": "NODE_ENV=prod node server.js",
  "start:dev": "NODE_ENV=dev node server.js"
},

read NODE_ENV in server.js either prod or dev

var env = process.env.NODE_ENV || 'prod'
    
if(env === 'dev'){
    var app = require("./serverDev.js");
} else {
    var app = require("./serverProd.js");
}

2 Comments

note that syntax like "start:prod": "NODE_ENV=prod node server.js" in package.json won't work on Windows, unless you use cross-env
Correction?: "start": "webpack-dev-server --port process.env.npm_package_config_myPort || 8080" }, should be "start": "webpack-dev-server --port $npm_package_config_myPort || 8080" }, according to my use explained by this tutorial. The process ref can be used within the javascript apparently.
98
+150

As of npm 2.x, you can pass args into run-scripts by separating with --

Terminal

npm run-script start -- --foo=3

Package.json

"start": "node ./index.js"

Index.js

console.log('process.argv', process.argv);

2 Comments

@KyleMit: you're probably right, though currently the answer is redundant, even if it might have been different back then. I'll delete all but my first comment.
For PowerShell users on Windows see answer For PowerShell users on Windows !
53

Use process.argv in your code then just provide a trailing $* to your scripts value entry.

As an example try it with a simple script which just logs the provided arguments to standard out echoargs.js:

console.log('arguments: ' + process.argv.slice(2));

package.json:

"scripts": {
    "start": "node echoargs.js $*"
}

Examples:

> npm start 1 2 3
arguments: 1,2,3

process.argv[0] is the executable (node), process.argv[1] is your script.

Tested with npm v5.3.0 and node v8.4.0

5 Comments

Does not work after adding -- to arguments, eg - npm run demo.js --skip, it works if added an extra --, eg - npm run demo.js -- --skip
Can you use this method without having a separate echoargs.js script file?
@JoshuaPinter echoargs.js is just meant as example, I'll edit my answer to make this clear
@Peter Right, but does it have to be a script file. I'm trying to create a script that uses adb to push a .db file to the Android emulator and accepts a param for the local path of the .db file to push to it, which is the first parameter of adb push. Something like this: "db:push": "adb push process.argv.slice(2) /data/data/com.cntral.app/databases/database.db" and I want to call it with npm run db:push /Users/joshuapinter/Downloads/updated.db. Any thoughts?
This solution does not work on windows. I am trying to pass two arguments like this: "app": "node src/app.js $*", but only the last argument gets passed. npm run app -r "file_name.csv"
46

For PowerShell users on Windows

The accepted answer did not work for me with npm 6.14. Neither adding no -- nor including it once does work. However, putting -- twice or putting "--" once before the arguments does the trick. Example:

npm run <my_script> -- -- <my arguments like --this>

Suspected reason

Like in bash, -- instructs PowerShell to treat all following arguments as literal strings, and not options (E.g see this answer). The issues seems to be that the command is interpreted one time more than expected, loosing the '--'. For instance, by doing

npm run <my_script> -- --option value

npm will run

<my_script> value

However, doing

npm run <my_script> "--" --option value

results in

<my_script> "--option" "value"

which works fine.

5 Comments

Oh my goodness you saved me so much time... so much time... and hair... and sanity... and wellbeing... and sanity... insane... self perseverance... going mad... ahhhhhhhhh
An alternative is to use --- triple slash
See github.com/npm/cli/issues/3136 -- -- works too
I didn't need the speechmarks "--" on Node version 19 but do need it on Node 22, could be corresponding npm version
@chantey You saved me. npm changed passing args from "--" to "---".
38

Most of the answers above cover just passing the arguments into your NodeJS script, called by npm. My solution is for general use.

Just wrap the npm script with a shell interpreter (e.g. sh) call and pass the arguments as usual. The only exception is that the first argument number is 0.

For example, you want to add the npm script someprogram --env=<argument_1>, where someprogram just prints the value of the env argument:

package.json

"scripts": {
  "command": "sh -c 'someprogram --env=$0'"
}

When you run it:

% npm run -s command my-environment
my-environment

Comments

27

If you want to pass arguments to the middle of an npm script, as opposed to just having them appended to the end, then inline environment variables seem to work nicely:

"scripts": {
  "dev": "BABEL_ARGS=-w npm run build && cd lib/server && nodemon index.js",
  "start": "npm run build && node lib/server/index.js",
  "build": "mkdir -p lib && babel $BABEL_ARGS -s inline --stage 0 src -d lib",
},

Here, npm run dev passes the -w watch flag to babel, but npm run start just runs a regular build once.

4 Comments

How is this called from the CLI?
@dresdin npm run dev, npm start
Need to use cross-env to use it on Windows.
Is there any way where result of first npm script store in some variable and use that variable in 2nd npm script?
7

This doesn't really answer your question but you could always use environment variables instead:

"scripts": {
    "start": "PORT=3000 node server.js"
}

Then in your server.js file:

var port = process.env.PORT || 3000;

1 Comment

This is good as long as you are on a Unix platform. Unfortunately it doesn't work with Windows as that has a convention of its own.
7

Separate your arguments using -- from the script and add all the required arguments, we can later access them by index.

npm run start -- [email protected] 100

You can get params in node using

const params = process.argv.slice(2);
console.log(params);

Output

['[email protected]', '100']

Comments

6

No need to assign values to variables. You can wrap this in a named bash function. And call the named function on the same line.

"scripts": {
    "start": "fun () { node './script.js' server $1; }; fun",
    "echo": "fun () { echo $*; }; fun"
}

Be mindful of semi-colins. They matter in this context. But basically content after the script would get dumped to the end of the script line.

npm run echo 1 2 3

As @peters answer mentions putting "$*" at the end would also work. that would look like the following:

"scripts": {
    "echo": "fun () { echo $*; }; fun $*"
}

However, "$*" is not required.
Environment: m1 mac, npm version 8.19.4, node version v16.20.2

Comments

4

I've found this question while I was trying to solve my issue with running sequelize seed:generate cli command:

node_modules/.bin/sequelize seed:generate --name=user

Let me get to the point. I wanted to have a short script command in my package.json file and to provide --name argument at the same time

The answer came after some experiments. Here is my command in package.json

"scripts: {
  "seed:generate":"NODE_ENV=development node_modules/.bin/sequelize seed:generate"
}

... and here is an example of running it in terminal to generate a seed file for a user

> yarn seed:generate --name=user

> npm run seed:generate -- --name=user

FYI

yarn -v
1.6.0

npm -v
5.6.0

6 Comments

Is this the same technique as that explained in the accepted answer back in 2013, to pass -- --arg1, ... ?
OK, then why repeat the answer?
If I wanted to share another example for a technique already explained in a different answer, I would add my example as a comment to that answer.
gotcha, will do that way next time
This answer boils down to "use yarn if you want to avoid -- when using flags" +1
|
4

Note: This approach modifies your package.json on the fly, use it if you have no alternative.

I had to pass command line arguments to my scripts which were something like:

"scripts": {
    "start": "npm run build && npm run watch",
    "watch": "concurrently  \"npm run watch-ts\" \"npm run watch-node\"",
    ...
}

So, this means I start my app with npm run start.

Now if I want to pass some arguments, I would start with maybe:

npm run start -- --config=someConfig

What this does is: npm run build && npm run watch -- --config=someConfig. Problem with this is, it always appends the arguments to the end of the script. This means all the chained scripts don't get these arguments(Args maybe or may not be required by all, but that's a different story.). Further when the linked scripts are called then those scripts won't get the passed arguments. i.e. The watch script won't get the passed arguments.

The production usage of my app is as an .exe, so passing the arguments in the exe works fine but if want to do this during development, it gets problamatic.

I couldn't find any proper way to achieve this, so this is what I have tried.

I have created a javascript file: start-script.js at the parent level of the application, I have a "default.package.json" and instead of maintaining "package.json", I maintain "default.package.json". The purpose of start-script.json is to read default.package.json, extract the scripts and look for npm run scriptname then append the passed arguments to these scripts. After this, it will create a new package.json and copy the data from default.package.json with modified scripts and then call npm run start.

const fs = require('fs');
const { spawn } = require('child_process');

// open default.package.json
const defaultPackage = fs.readFileSync('./default.package.json');
try {
    const packageOb = JSON.parse(defaultPackage);
    // loop over the scripts present in this object, edit them with flags
    if ('scripts' in packageOb && process.argv.length > 2) {

        const passedFlags = ` -- ${process.argv.slice(2).join(' ')}`;
        // assuming the script names have words, : or -, modify the regex if required.
        const regexPattern = /(npm run [\w:-]*)/g;
        const scriptsWithFlags = Object.entries(packageOb.scripts).reduce((acc, [key, value]) => {
            const patternMatches = value.match(regexPattern);
            // loop over all the matched strings and attach the desired flags.
            if (patternMatches) {
                for (let eachMatchedPattern of patternMatches) {
                    const startIndex = value.indexOf(eachMatchedPattern);
                    const endIndex = startIndex + eachMatchedPattern.length;
                    // save the string which doen't fall in this matched pattern range.
                    value = value.slice(0, startIndex) + eachMatchedPattern + passedFlags + value.slice(endIndex);
                }
            }
            acc[key] = value;
            return acc;
        }, {});
        packageOb.scripts = scriptsWithFlags;
    }

    const modifiedJSON = JSON.stringify(packageOb, null, 4);
    fs.writeFileSync('./package.json', modifiedJSON);

    // now run your npm start script
    let cmd = 'npm';
    // check if this works in your OS
    if (process.platform === 'win32') {
        cmd = 'npm.cmd';    // https://github.com/nodejs/node/issues/3675
    }
    spawn(cmd, ['run', 'start'], { stdio: 'inherit' });

} catch(e) {
    console.log('Error while parsing default.package.json', e);
}

Now, instead of doing npm run start, I do node start-script.js --c=somethis --r=somethingElse

The initial run looks fine, but haven't tested thoroughly. Use it, if you like for you app development.

Comments

4

For me, on Windows 11, using the variable with ${npm_config_VARNAME} didn't work, I needed to use it with %npm_config_VARNAME% (changing ${} to %%), like this:

{
  "name": "yourproject",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "sass": "sass scss/%npm_config_file%.scss dist/css/%npm_config_file%.css -w"
  },
  "author": "",
  "license": ""
}

And on terminal:

npm run sass --file=front-page

And it worked.

Comments

3

I find it's possible to just pass variables exactly as you would to Node.js:

// index.js
console.log(process.env.TEST_ENV_VAR)
// package.json
...
"scripts": { "start": "node index.js" },
...
TEST_ENV_VAR=hello npm start

Prints out "hello"

Comments

3

Somehow I found these answers were not solving my problem so I will show an example of my problem and my solution.

I had an npm task to run the ci tests in newman and I wanted another npm task to run them per folder. There is a flag for that in newman (--folder ). So I had in my npm scripts:

"test:api": "newman run test-collection.json -e environment.json"

And I wanted to run the same command but passing it the folder option so that I could also run that command or another command with the folder option, so I did:

"test:api:folder": "npm run test:api -- --folder"

So with this I can ran all the tests with:

npm run test:api

Or ONLY the tests in a specific folder with:

npm run test:api:folder <folder-name> 

I hope it helps, cheers!

Comments

1

I settled for something like this, look at the test-watch script:

"scripts": {
    "dev": "tsc-watch --onSuccess \"node ./dist/server.js\"",
    "test": "tsc && cross-env NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 jest",
    "test-watch": "cross-env NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 tsc-watch --onSuccess",
  },

You invoke the test-watch script like this:

// Run all tests with odata in their name
npm run test-watch "jest odata"

Comments

0

From what I see, people use package.json scripts when they would like to run script in simpler way. For example, to use nodemon that installed in local node_modules, we can't call nodemon directly from the cli, but we can call it by using ./node_modules/nodemon/nodemon.js. So, to simplify this long typing, we can put this...

    ...

    scripts: {
      'start': 'nodemon app.js'
    }

    ...

... then call npm start to use 'nodemon' which has app.js as the first argument.

What I'm trying to say, if you just want to start your server with the node command, I don't think you need to use scripts. Typing npm start or node app.js has the same effort.

But if you do want to use nodemon, and want to pass a dynamic argument, don't use script either. Try to use symlink instead.

For example using migration with sequelize. I create a symlink...

ln -s node_modules/sequelize/bin/sequelize sequelize

... And I can pass any arguement when I call it ...

./sequlize -h /* show help */

./sequelize -m /* upgrade migration */

./sequelize -m -u /* downgrade migration */

etc...

At this point, using symlink is the best way I could figure out, but I don't really think it's the best practice.

I also hope for your opinion to my answer.

1 Comment

This doesn't answer the question at all. I don't know how it got 6 upvotes, but congrats :)
0

I know there is an approved answer already, but I kinda like this JSON approach.

npm start '{"PROJECT_NAME_STR":"my amazing stuff", "CRAZY_ARR":[0,7,"hungry"], "MAGICAL_NUMBER_INT": 42, "THING_BOO":true}';

Usually I have like 1 var I need, such as a project name, so I find this quick n' simple.

Also I often have something like this in my package.json

"scripts": {
    "start": "NODE_ENV=development node local.js"
}

And being greedy I want "all of it", NODE_ENV and the CMD line arg stuff.

You simply access these things like so in your file (in my case local.js)

console.log(process.env.NODE_ENV, starter_obj.CRAZY_ARR, starter_obj.PROJECT_NAME_STR, starter_obj.MAGICAL_NUMBER_INT, starter_obj.THING_BOO);

You just need to have this bit above it (I'm running v10.16.0 btw)

var starter_obj = JSON.parse(JSON.parse(process.env.npm_config_argv).remain[0]);

Anyhoo, question already answered. Thought I'd share, as I use this method a lot.

3 Comments

npm_config_argv was removed as of npm v7 github.com/npm/cli/issues/1995
I just went in and checked that, I am now bumped to v14.9.0. It still works for me. process.env.npm_config_argv is only undefined 'until' you run the npm start with all the passed info.
Please note that nodejs version is one thing and npm version another. See table here nodejs.org/en/download/releases to check what nodejs version has what npm version included. Nodejs v14 has still npm v6 so that is why it works for you (so far, until upgrade to nodejs v15 with npm v7)
0

i had the same issue when i need to deploy to different environments here is the package.json pre and post the updates.

 scripts:
{"deploy-sit": "sls deploy --config resources-sit.yml",
 "deploy-uat": "sls deploy --config resources-uat.yml",
 "deploy-dev": "sls deploy --config resources-dev.yml"}

but here is the correct method to adopt the environment variables rather than repeating ourselves

scripts:{"deploy-env": "sls deploy --config resources-$ENV_VAR.yml"}

finally you can deploy by running ENV_VAR=dev npm run deploy-env

Comments

0

Using npm 9.3.1, you can pass variables to scripts by this way:

Inside "scripts" tag in package.json, put the variable with "$"

  "scripts": {
...
    "generate": "nest g controller $NAME && nest g service $NAME && nest g module $NAME"
  },

When you call the script, just pass the variable with her value in the console before the script

 NAME=auth npm run generate
 or
 NAME=auth yarn generate

In this example, the script will generate controllers, services e modules with the name passed for an Nest application.

Comments

-1

npm run script_target -- < argument > Basically this is the way of passing the command line arguments but it will work only in case of when script have only one command running like I am running a command i.e. npm run start -- 4200

"script":{
       "start" : "ng serve --port="
 }

This will run for passing command line parameters but what if we run more then one command together like npm run build c:/workspace/file

"script":{
       "build" : "copy c:/file <arg> && ng build"
 } 

but it will interpreter like this while running copy c:/file && ng build c:/work space/file and we are expected something like this copy c:/file c:/work space/file && ng build

Note :- so command line parameter only work ad expected in case of only one command in a script.

I read some answers above in which some of them are writing that you can access the command line parameter using $ symbol but this will not gonna work

Comments

-1

Try cross-env NPM package.

Easy to use. Easy to install. Cross all platform.

Example:

  1. set arguments for command
// package.json
"scripts": {
  “test”: “node test.js”,
  “test-with-env-arg”: “cross-env YourEnvVarName=strValue yarn test,
}
  1. get arguments from process.env
// test.js
const getCommandLineArg = Boolean(process.env.YourEnvVarName === 'true')  // Attention: value of process.env.* is String type, not number || boolean

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.