Recently, our organization planned to migrate an internal application web API to Nodejs web API. We have everything to work on node app, but we were lacking a console where we can run queries and examine results to implement them in our controller or vice-versa.

fig 1. Nodejs console

What we have in Ruby on Rails framework

rails_app $ rails c
irb(main):005:0> User.first
User Load (0.7ms)  SELECT  "users".* FROM "users"  ORDER BY "users"."id" ASC LIMIT 1
=> #<User id: 1, email: "aravind@kiprosh.com", created_at: "2018-11-29 07:56:26", updated_at: "2018-11-29 07:56:26">

Problem

In the existing node console, we need to require each model to perform an operation and sometimes we get an error when we run complex queries like includes or joins if we forget require/assign that specific model, one such error I encountered as follows

$ node
> models = require('./app/models')
> User.findOne().then(u => user = u)
ReferenceError: User is not defined
> // Oops! forgot to assign User = models.User

After googling found out that we can modify REPL and set up some configuration before REPL context begins.

Solution

To modify REPL add a file to the root folder of your app and name it console.js as shown in script 1. console.js

script 1. console.js

To make it clear here is our dir structure,

root
|- app
|   |- models
|       |- index.js
|       |- User.js
|- lib
|   |- DateTime.js
|- console.js
|- package.json
|- server.js
|- yarn.lock

console.js

Let’s see what’s going on in console.js

As you can see on line no 28 (in script 1), first we need to require nodejs REPL, To know more about Nodejs REPL visit https://nodejs.org/api/repl.html.

on line no. 29, we require sequelize models as we are using sequelize ORM in our project. The models will now have an object with key, value pairs of our database models and all other logical stuff of ORM. But, we can access our models directly using models which we required.

$ node
> const models = require('./app/models')
undefined
> User = models['User'] // models.User will also works
undefined
> User.findOne().then(u => user = u) // Promise
> user.toJSON() // will return {id: 1, email: "user@email.com", ....

global is a Global object of nodejs. In browser we have window as a global object likewise, In nodejs we have global named variable as global object

fig 2. browser window object
fig 3. Nodejs global object

Before modifying global objects we need to make sure that we are not breaking existing global object properties.

Hence, on line no. 31(script 1. console.js) we fetching keys of models object and assigning on global object, now when we type User in console we get as follows

$ node
> User
User { ...

on line no. 35, we are assigning DateTime object, which is custom tailored class to handle date objects in our project.

on line no. 37, we start the repl server as follows

let replServer = repl.start({  prompt: 'app > '});

on line no. 39, we are assigning models to a db variable so later we can access our models using db variable. The output of replServer.context will be shown in fig. 4, it will have global object and all other required objects.

fig 4. console.log(replServer.context);

Now run console.js using command

$ npm run console

OR we can run using yarn

// package.json
"scripts": {
  "console": "node run console"
}

$ yarn console
app > user = await User.findOne()
Executing ....
app > user.toJSON()
{ id: 241,
  first_name: 'Aravind',
  last_name: 'Vemula',
  email: 'aravind@kiprosh.com',
  password_hash: '$2b$10$eTnpwZuLfAdcg...',
  company_id: 1,
  inserted_at: 2018-11-29T01:00:46.846Z,
  updated_at: 2018-11-29T01:00:46.846Z,
  nickname: 'AravindVemula',
  deleted_at: null,
  timezone: 'Asia/Calcutta',
  reset_password_token: null,
  reset_token_sent_at: null,
  profile_pic_url: null,
  default_project_id: null }
app >

Modify console.js to add more functionality, like reloading console whenever a file change happens etc.