Build Your Own App Specific REPL For Your NodeJS App

Nicole Sullivan asked a question on twitter: 

I was immediately intrigued by the possibility of having an equivalent of a “rails c” for my NodeJS apps, so I started digging in.

App specific repl

Rails C? IRB? What?

In case you’re not familiar with Ruby on Rails, the idea of “rails c” is that it will open a command line REPL interface with all of your Rails application gems and environment configurations loaded and ready to roll. This is a very powerful tool for debugging and working with Rails apps. I used it A LOT when I was a rails dev.

The “irb” command is the ruby REPL environment, on which “rails c” is built. Of course there is an equivalent of irb in NodeJS: running “node” by itself will open the NodeJS REPL interface. This allows you to run any NodeJS command that you want, directly inside of your console.

Ok, great. NodeJS has a REPL interface that is the equivalent of irb. But what about the rails c equivalent? I don’t know of a built-in equivalent for any NodeJS app frameworks off-hand… but after a few minutes of googling and some trial and error, I found out just how easy it is to build your own application specific REPL interface. 

Build Your Own REPL

It turns out the REPL interface in NodeJS is a module that is built in to NodeJS core, and you can create your own instance of it and play with it to your heart’s content! Check out the basic documentation on the NodeJS site for some quick info on it.

The gist of what you need to do is require the “repl” module and then “repl.start” to start a session. You need to provide a prompt for the start method to use, as well. Let’s use “my-app >” as the prompt.

The result, when you run “node my-repl.js” looks like this:

This is a great start and it shows that you can build your own REPL… but what about customizing it for your application environment?

Customizing The REPL Context

The core of what “rails c” does, other than starting the REPL environment, is loading the application gems and configuration. So let’s do the same thing with our custom REPL for NodeJS and create a REPL that is specific to our application.

The only thing I need to do is attach my application specific modules to my REPL interface. This is done easily by attaching attributes to the “context” object on the REPL object that is returned from the “start” method.

Now when I run my repl.js file, I can instantly access these “context” variables:

Having that in hand, let’s build a custom REPL interface for an application.

Building An App Specific REPL

In my case, I have a lot of custom modules that I built for my SignalLeaf service. These modules include Accounts, Podcasts, amazon integration, and much more. Furthermore, I already have a lot of command-line “reports” that I use for SignalLeaf. These reports each load up the environment configuration, connect to the database and run some code to produce a text based report… and the majority of this code can easily be re-used in my SignalLeaf specific REPL file.

So I’ll take the code that creates my environment configuration variables, requires in all of my app specific modules and connects to the database, then add this to my custom REPL file. I’ll start up my database connection first and once it is connected, I’ll open the REPL session and include all of the custom modules in the REPL context. And for a bit of vanity, I’ll customize the REPL prompt to give me my project name and current environment configuration.

The result is an instant application specific REPL interface, that is now the (close enough) equivalent of a “rails c” console! I can run my application code directly in my REPL interface without having to manually require the modules and instantiate the environment!

Is This Really Useful?

YES! A THOUSAND TIMES, YES! This is a HUGE win for me!

I’ve had countless hours spent in NodeJS console REPL, loading and re-loading all of my app modules. Having this consolidated in to a single file that I can run any time I want, and customize however I need, is an epic win of epic proportions. I will literally save hours of effort every month, as I am now able to load up my entire environment and run any arbitrary code I want, without having to copy and paste a file full of configuration each time I want to do something slightly different and test out some code.

#WINNING!

 

 

PS: If you’d like to see the NodeJS REPL interface, check out the WatchMeCode video writing your first NodeJS code – it’s the free video that I’ve posted on the homepage of WatchMeCode.net

NewImage


Get The Best JavaScript Secrets!

Get the best kept secrets of JavaScript, and the most important career advice you'll ever hear!
Don't miss out: join my list to get all the inside info!

  • http://www.kestrelblackmore.com/ Kestrel Blackmore

    Nice work man!! nnn”rails c” has definitely helped me solve a bunch of things in production. Awesome that you’ve figured out a node equivalent.

    • http://mutedsolutions.com Derick Bailey

      thanks! this definitely feels like a big win for me. i’ve been using it already, just in the few hours since i figured this out! :D

      • http://www.kestrelblackmore.com/ Kestrel Blackmore

        I wonder if there is something like this for .NET?

  • Stratos Pavlakis

    this is great! good work.

  • http://createbang.com/ Michael Phillips

    Super helpful. Thanks for the write-up!

  • Aftab Alam

    Great, great, great…Amazing and informative like always. Thanks!

  • http://datahero.com/ Islam Sharabash

    Ha – coming from a Rails background too I did the same thing.nnA couple useful context functions I have in mine (“r” is my replServer).nnThe first “print” function you can use as a callback to inspect what’s passed back.nUse with any asnyc function: fs.readFile(‘/foo’, print)nnr.context.print = function() {n console.log(JSON.stringify(arguments, null, 2));n};nnnThe second let’s you clear the require cache, so you can change files and re-require on the fly.nUse:nvar thing = require(‘thing’);n// Change something in thingnunload(‘thing’);nvar thing = require(‘thing’);nnr.context.unload = function(name) {n var path = require.resolve(name);n delete require.cache[path];n};

    • http://mutedsolutions.com Derick Bailey

      nice! definitely going to add these to my setup. thanks :)

  • Stephan Hoyer

    Did this two years ago already! https://www.npmjs.org/package/chuk

  • mayhap

    hapijs (Which you should be using: http://hapijs.com) ships with a plugin for a REPLnnhttps://www.npmjs.org/package/reptile

  • moshir mikael

    Nice. But how do you deal with async calls from REPL ?nI guess your app specific modules are exposing async methods.nSo whenever you would type in Podcasts.find(), node REPL would exit immediately, and you would never get the result.

    • http://mutedsolutions.com Derick Bailey

      that’s certainly a bit tricky, but still possible. you will get the result, but not the way you expect. you have to provide callbacks that do things like set global vars and console.log a result to tell you it’s ready. nn> var myData = undefined;nn> foo.bar(function(data) { myData = data; console.log(“data here”); });nna moment later, you will see “data here” on the console, and then you can access “myData” and see what it returned.nni’ve done this, and it works. it’s just a bit ugly.