Node.js REPL - run js code within your app environment - JSCasts episode 12

12 episode of JSCast with Wojciech Krysiak!

Code for the video available here: https://github.com/JSCasts-episodes/ep12-node-repl

The script

In this episode, I will tell you how you can use Node Interactive Shell to run javascript code within your app environment.

Example app

We will be working on an example express server which has 2 resources: users and their favourite places.

The application uses MongoDB with mongoose client library for storing data.

The idea of the application is that only a finite set of users can use it. You cannot register new members by using the API.

So how to add the first users, in this case?

As you can see, Users have a password field, which is encrypted, so you cannot simply add new records straight to the database.

...
const User = model('User', {
  name: String,
  email: String,
  encryptedPassword: String,
})

User.create = async ({ name, email, password }) => {
  const user = new User({ name, email })
  user.encryptedPassword = await Bcrypt.hash(password, 10)
  return user.save()
}
...

But we see that there is a `create` function which can be used inside the app.

REPL

So let’s invoke this function by using a so-called REPL which stands for Read Eval Print Loop.

You can launch it by simply running the “node” command in the app folder.

node

Now we have a new process running with the same set of dependencies as we have in our app.

So let’s connect with the database. Require mongoose first, and connect:

const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost:27017/ep12-repl').then(() => console.log('connected'))

I resolve the promise with console.log

Having that, we can now create a new user. Let’s require the User model, and invoke our function. In REPL we can use the tab key to see autocompletes.

User. <TAB>

In the list of all the properties of the User model there is our ‘create’ method.

NODE.JS REPL - user.create

Let's write the first 3 letters and use tab again.

Function takes a name, email and password. Since it is an async function it returns a promise. I fill it with my credentials and console.log the output

User.create({ name: 'Wojtek', email: 'w@softwarebrothers.co', password: 'password123'}).then((out) => console.log(out))

And now we have one new user.

OK - as you probably noticed we used promises instead of async/await statements. Node has experimental support for that - let’s turn this on.

To exit the REPL hit ctrl+c twice.

Now let’s run it with async support.

node --experimental-repl-await

By default, node stores history of your previous commands so you can use up/down arrows to find them.

So let's connect with the database…. And require our User

const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost:27017/ep12-repl').then(() => console.log('connected'))
const User = require('./src/models/user.model')

Now we can count all Users in the database with an await statement.

await User.countDocuments()

Works! Lets exit, again by using ctrl+c twice

REPL API

Most probably we will be performing the same initialization every time we will use REPL so let’s automate that by creating our own Interactive Shell.

In order to do that add a new, repl, script to the package.json file

...
"scripts": {
	"repl": "NODE_OPTIONS=--experimental-repl-await node repl.js"
}
...

Since we will be using REPL API we have to pass experimenta-repl-await flag via NODE_OPTIONS.

And create our repl.js file. Require REPL first and start it with the new prompt message, which will be “jscasts”:

const repl = require('repl')
repl.start('jscasts > ')

Let’s run it and see if it works. Perfect. Close it for now.

Now let’s initialize database every time we launch it. First reuse our .env file and load all variables with dot.env config. I covered .env files in episode 6. so if you didn’t have a chance to see it, it may be worth to check it out.

Now create async run function. Next, add mongoose and connect with the database within this function. Finally, use context property to pass our model to the REPL console, this way we won’t have to require it every time.

And invoke this function.

// repl.js
require('dotenv').config()
const repl = require('repl')
const mongoose = require('mongoose')

const run = async () => {
  await mongoose.connect(process.env.MONGO_URL, {
    useNewUrlParser: true
  })
  const r = repl.start('jscasts > ')

  r.context.User = require('./src/models/user.model')
}

run()

Let’s see how it works right now. Let’s try to count all Users again.

await User.countDocuments() // => 1

It works.

REPL tricks

There are a couple of tricks when working with a REPL command. First of all, you can enter the editor mode with .editor command.Here you can use or paste the entire block of code…. and execute it by pressing Ctrl + D

So let’s see how that works:

Create a variable. Use if for storing number of documents. And finally increment it.

var a
a = await User.countDocuments()
a = a + 1

Then run Ctrl + D.

And here is our output: 2

The next thing is that there are 2 special variables

_ (Underscore) - holding the output of the last command.

So use that to catch our output.

And there is an _error - which holds the last thrown error.

Ok - this was everything. Now you can use your interactive node shell and check out how the application works with real data and easily find and fix problems.

To discover more things about the REPL - visit the documentation

If you liked this pots - subscribe the channel and see you next time!