Writing interactive command line apps in JavaScript with Node.js, or how to write the most classic type of software on the most modern framework without frying your brain in the process

THE PROBLEM

Say you want to write a little command line application. For example, a simple app that greets you, asks for your name, and then talks to you by your name. Here’s a sample session showing how your intended app looks like when used (user-typed input shown in red):

hello-whats-your-name

But here’s the catch: you want to write this using JavaScript. Maybe JavaScript is your favorite language, or possibly you’re trying to learn it in order to develop web sites. In any case — you start researching, and it seems not-that-easy to use JavaScript outside the web browser. JavaScript was born in and for the web, and it has never felt very at home at the command line. But not all is lost…

For a few years now, you can famously use something called Node.js to write JavaScript apps that run outside the browser. And it’s not a toy: it has a huge community, almost 100,000 reusable open-source packages neatly available via its npm package manager, and it is being successfully used to create the back-end of many popular web sites! Not only that, IDEs like Netbeans or Webstorm support it directly, so creating a first simple Node.js application is as simple as selecting “New project -> Node.js”. It seems you are getting closer to fulfilling your little command-line app writing goals! Surely, the code will just look something simple like this:

Right, right? So you start writing it and researching all things Node.js… soon enough, you find out that writing a line is actually done using console.log(), great. But how do you read what the user types? Not easy to find, so more searching is needed…

…jeez, no trace of readLine or anything similar on the Node.js library, by any reasonable name you can think of…

…searching online, readLine is nowhere to be found…

…some mention of a JavaScript readLine function for Rhino, but Rhino doesn’t seem too active, and mainly, it is not Node.js or compatible with it…

…and you keep searching…

…where the @*#! is readLine?

Well, after some time, you end up picturing the answer: it turns out that Node.js uses an awesome “event-based model”, and this requires that any program that interacts with anything be written using “closures”, “callbacks”, “events” and what not. A monster. It is so bad that, to write the above sample app, you end up needing to read articles with titles like “Demystifying events in Node.js”. Node.js might be awesome, but if writing a simple app like the above needs a lot of demystifying, we may need to part ways…

(Note: I am not reproducing the code Node.js would need you to write the app above using just its out-the-box functionality – it’s “hardcore programming porn” and this blog would become NSFW.)

So all is lost, right? Nope! I applied some obscure programming superpowers called “coroutines” and built a good solution you can use easily! The best thing is that, fortunately, you can use it without needing to know what “coroutines” even means. Read on for the details.

THE SOLUTION

Here is how you can use the solution I concocted:

1) I created a Node.js package called “nicl”, which stands for “Node.js Interactive Command Line”. I pronounce it like “nickel”. Here is nicl on github and here is nicl on npm. You don’t really need to check those links, just go ahead and install it from your command line:

2) Write code like the following for your little app (note the use of nicl):

3) You’re done! Run it from the command-line with “node main.js” or debug it inside NetBeans, even typing and interacting in the console window. It all works as expected!

For now, I’ve tested nicl to work nicely on OS X, for educational interactive apps, allowing simple line-wise input. It should also work in Windows and Linux, though. In the future it may gain more functionality such as raw “keypress” input, and become valid for production work – it may already be, but it just doesn’t have enough usage and testing to be considered such.

But meanwhile, enjoy!

SPILLING THE TECHNICAL GUTS

So you are of the type who needs to know how things work, beyond just using them? Sorry to hear that, I know first-hand how inconvenient that is for life at large. In any case, here are the details to satisfy your tech gore cravings. It nearly took me less time to actually implement the thingy than what I spent understanding the situation by reading a dozen articles on command-line JavaScript and Node.js.

The main reason I was researching this is to allow someone to take their first steps as a developer by writing simple command-line apps in JavaScript, so to be viable, a solution needed to be really simple to use. Simplicity is probably the most important design principle anyway, so this requirement also improves things for production.

So yes, it’s true, Node.js doesn’t allow “sequential code”, and your program can’t block waiting for anything like a keypress — well, technically it can, but then all of Node.js blocks, even the part that detects keypresses, so the application hangs forever. Thus, out of the box, doing nearly anything in Node.js requires advanced computer science concepts like “closures”. Not simple. Not educational. Not for beginners. Bummer.

I dislike advanced compsci concepts with my daily coding as much as the next guy, but that doesn’t mean they aren’t handy sometimes. So the whole thing reminded me of a thing called “coroutines”, which allows you to “suspend” one calling function temporarily and then resume it when something is ready. So I Googled for “Node.js coroutines”…

…and a few minutes later I had found the awesome fibers package by Marc Laverdet, bringing coroutines to Node.js with the name of “fibers”. His main example showed how an application can seemingly “block” for a few seconds by calling a fiber-powered “sleep()” function, without actually blocking Node.js. If fibers could be used to wait for a few seconds without blocking input, it could most likely be used to wait for input itself…

…bang, it worked like a breeze. A few hours later, I had made nicl available on github and npm. The code is simple, have a look. Along the way, I learned how to release a package using npm, which is the best source code publishing experience I’ve ever had, and I’ve tried a few. Congrats node & npm folks. And I was even so fortunate to get Marc, the author of fibers, to check my code and suggest some improvement to it. So now I got some sort of blessing from the Node.js experts themselves. Thanks Marc!

CLOSING THOUGHTS

When learning anything, the right progression of concepts and steps is the best guarantee for both faster and more solid learning. Specifically for programming, I believe that the best way for any newcomer to start is to first grasp the basic concepts, in isolation of the many complexities of real software. The first sessions should talk only about code execution, statements, variables, conditions, and loops. Only then, all the rest! And a beginner should also be as isolated from “magic boilerplate code” as possible. Thus, my best recipe to start learning to code is to write many varied command-line apps, initially without user interaction, and after that, just by asking the user to respond to simple prompts. Once you can write these things with one hand tied behind your back, it’s the moment to move on to real applications with a graphical user interface.

If, as an aspiring developer, the web is your thing, you should learn JavaScript. JavaScript has been with us for 20 years, and thanks to the universal reach and huge momentum of the web, it will be with us at least 20 more. It is here to stay. And I’m sure many people can have a very productive development career using only JavaScript. Web front-end, web back-end, and probably many other environments.

It follows that learning JavaScript by writing little command-line apps should be a viable kickstart for a successful development career…

…but Node.js trickery was trying to get in the way of this great start…

…until nicl, which fixes that.

Happy coding.

— Jon Beltran de Heredia, Barcelona, March 2016