Previously we’ve looked at how to start debugging a simple server and the ways you can move around the code in the debugger. Today we’ll check out two less prominent features. The first one is the ability to edit variables. The second being the ability to replay a function - to restart a stack frame, to be precise - without restarting the debugger or the application.
While these features might not be super intuitive, they are quite powerful and can save a ton of time. You can try a function buried deep in the call stack with different inputs, without restarting a potentially long and tedious journey.
Let’s see how we can use them together to try different scenarios in our usual tiny server app.
Setup
We’ll play around with the usual code snippet, go ahead and copy-paste it into an empty file if it’s not on your machine yet:
const http = require('http');
const url = require('url');
const hostname = '127.0.0.1';
const port = 3456;
const serverUrl = `http://${hostname}:${port}`
const getNameFromReq = (req) => {
const {name} = url.parse(req.url, true).query;
return name
}
const getGreeting = (name) => {
const greeting = `Hello, ${name}!`
return greeting
}
const server = http.createServer((req, res) => {
const name = getNameFromReq(req)
const greeting = getGreeting(name)
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end(`${greeting}\n`);
});
server.listen(port, hostname, () => {
console.log(`Server running at ${serverUrl}`);
});
Hello, undefined!
Our toy server assumes there is always a query parameter called name
present in the request. If it’s missing, the response becomes “Hello, undefined!”:
> curl http://127.0.0.1:3456
Hello, undefined!
Let’s use the debugger and see how our getGreeting
function behaves with different name
parameters. The fix is trivial, obviously, but let’s play along, we’re here to see what the debugger can do. 🤓
Let’s put a breakpoint on line 15 and hit the server without a name query argument (e.g. curl http://127.0.0.1:3456
).
You should see the debugger kicking in and stopping on line 15.
If you’re not confident how to do this, please refer to Part 1, the basics
Make sure the Variables and Call stack sections are both open in the debug sidebar. We’ll focus on the local variable name
for now, which is undefined:
Let’s step over the next line (F10
) and observe the return value of getGreeting
:
If you need a quick overview of step over, step into and step out, read Part 2, Navigating with steps
Ok, no surprises here: if the name
is undefined
, the greeting will say Hello, undefined!
.
Rewind
Let’s re-run this function, this time with a different name
. We don’t need to fire off another request - we can restart the last stack frame:
And we’re back at the beginning of the getGreeting
function 👌
Let’s now try what would happen if the name was null
? We can edit the local variable to fund out:
We can try to set a string value - mind the quotes around the value:
You can override anything local or defined in the closure, including functions:
Keep in mind: once you override functions you’ll have to restart the process to get back to the original implementation.
And this is it: you are now able to restart functions in the debugger and edit local and closure values, including strings and functions. Why not play around and see what happens if you restart functions higher up in the stack?
Happy debugging! 🥼