Mastering the Node.js REPL (part 3)
In the second part of this series we covered the Node.js REPL programatic API. This will be the last part of the series and we will write an example express application that takes advantage of that programatic API.
Previous posts:
All the code for this article lives in this repository. I strongly recommend you to check it out to follow me in this article.
The express application
We are going to use a naive express application that has a service layer and a controller layer. We won’t go into much detail about the express application but the project structure looks like this:
There is a data.js
file that uses faker to generate some mock data. The data will be 20 users and 100 posts associated to these users. The users will have an id
, a name
and an email
and the posts will have an id
, a title
a body
and an userId
.
For the sake of simplicity the service layer will only have a bunch of read functions and the controllers will use those services to return the data.
The main script mounts the router and starts the express application:
You can check the full code for the express application here.
Adding a repl to the project
Now that we have created our basic express application we can add a REPL to the project. We make a dev/repl/
directory and throw in there our code.
This is the code for dev/repl/repl.js
:
If you remember the part 2 of the series this code should sound familiar. The steps we follow are:
- Define our context initialiser function.
- Print a welcome message.
- Start the REPL with our custom prompt.
- Define our commands.
- Bind our event handlers.
The context initialiser clears the require
cache and re-requires some modules when the clear
command is invoked. This feature allows us to reload our modules if we make changes to them without having to restart the REPL.
We can also bind any library to the context for testing purposes just like we did with ramda
there.
Lastly we add a new script to the package.json
file to run the repl:
yarn repl
will now start the REPL session and load our stuff:
Connecting to a node process using sockets
We are now going to go one step further with one cool trick. Using the net node library, we are going to build a REPL server and start it alongside our express server. This will allow us to connect to the server’s running process and do anything we want in there.
The server code
We moved our dev/repl/
code to repl/
. The code for the net
server is straightforward:
We create a new server and invoke the repl
function passing the socket. The repl
function starts a new REPL and handles the socket events:
input
and output
are streams that the REPL uses to read from and to write to. input
is a stream.Readable
and output a stream.Writable
. Sockets are duplex streams, so we can use it for both input
and output
.
We have to handle a couple of extra cases: When the REPL exits we close the socket and if there is an error in the socket we just log it.
Writing our client code
Now that we have the server, we need to write the client. We are going to write a node script that receives a <HOST:PORT>
as its first argument and using the net
library again connects to the running server.
We need the socket to read from process.stdin
and to write to process.stdout
. We can do that by piping process.stdin
stream to the socket and the socket to process.stdout
.
On connect we set raw mode to true
. This will force special terminal characters and combinations like CTRL+C
to not be processed, so they can be sent through the socket and handled by the server.
To conclude, we close the socket when the process exits and we exit the process when the socket closes.
We change our package.json
to include a script that runs the client and we are ready to go:
We start the server:
We connect to it using the client:
And that’s it! We are running the REPL in the same process that our express server is running in and we can share server state or functionality with it.
Wrapping it up
In this last post of the series we went a bit further and wrote a REPL server for our express application. Keep in mind that this is a naive implementation intended for educational purposes only and we don’t even have any form of authentication or authorization.
Congratulations if you made it this far and I hope these posts have been useful to you. You can check out our medium publication if you want more awesome content from us.