yargs –do-things-right

Sometimes the obvious, default API is never what you want. That’s when you have to memorize the “do things right” flag.

For yargs, the command-line argument parser for Node.js, specify options and subcommands like this:

import * as yargs from "yargs";

const argv = yargs
    .option("org", {
      alias: ["o"], // one-letter version
      description: "tell them what this org thing does",
      type: "string",
    .command("do-stuff", // the subcommand
      "do the thing",  // description of the subcommand
      () => {},  // builder function: only do something here if the subcommand has its own special arguments
      /* next comes the thing you always want: the function to run the subcommand */
      function ({ org }) { // it receives a structure with the arguments parsed so far
        doTheThing(org).catch(handleError); // if you're doing promise-related stuff, be sure to handle exceptions
    .demandCommand() // require a subcommand
    .strict() // require a VALID subcommand, and only supported options
    .help() // the coolest part of yargs is that it builds the usage output, but only if you tell it to
    .argv; // trigger the parsing. If you forget this, it does nothing

There. Next time I can cut and paste this instead of scouring the internet for how to make yargs do things right.

Careful: it’s confusing to get the command’s handler function in the right place, since you have to put an empty “builder” function in front of it. I rarely want subcommand arguments; I always want the subcommand to do something. Skipping that That builder function does get executed when the subcommand is parsed, so if you get this wrong, the subcommand code seems to work, but not quite1.

So that future-me can find this again, things I googled while figuring this out:

  • yargs require command
  • yargs require valid command
  • yargs handle command
  • yargs show usage
  • yargs actually parse the arguments dammit
  • yargs do things right

[1] If you put your handler in the third argument (as the builder function), it’ll run that, and also maybe give a parsing error (if there’s some other argument it doesn’t like), cutting off the execution of the builder function if it’s asynchronous. So it looks right when you try it, but isn’t.