REPL & Basic Debugging

📖 Concept

The REPL (Read-Eval-Print-Loop) is Node.js's interactive shell — a powerful tool for experimenting, debugging, and exploring APIs in real time.

Starting the REPL:

$ node          # Start REPL
> 2 + 3
5
> .help         # Show REPL commands
> .exit         # Exit REPL

REPL special features:

  • _ — Contains the result of the last expression
  • Tab completion — Press Tab to see available properties/methods
  • Multi-line input — Automatically detects incomplete expressions
  • .load filename — Load and execute a file in the REPL
  • .save filename — Save the REPL session to a file
  • .editor — Enter multi-line editor mode (Ctrl+D to execute)

Debugging Node.js applications:

Method 1: console debugging (quick & dirty) console.log, console.error, console.table, console.dir, console.trace.

Method 2: Node.js Inspector (built-in debugger)

node --inspect app.js          # Start with inspector on port 9229
node --inspect-brk app.js      # Break on first line

Then open chrome://inspect in Chrome or use VS Code's debugger.

Method 3: VS Code debugger (recommended) Create .vscode/launch.json with a Node.js configuration. Set breakpoints in the editor and press F5.

Method 4: debugger statement Place debugger; in your code — it acts as a breakpoint when running with --inspect.

The util.inspect() function: Deeply inspect objects with control over depth, colors, and hidden properties. Unlike JSON.stringify, it handles circular references, Symbols, getters, and class instances.

Error handling basics: Node.js has two categories of errors:

  1. Operational errors — Expected failures (network timeout, file not found, invalid user input). Handle with try/catch, error callbacks, or rejected promises.
  2. Programmer errors — Bugs in code (TypeError, ReferenceError, assertion failures). Fix by correcting the code.

🏠 Real-world analogy: The REPL is like a chemistry lab — you can mix reagents (code) and immediately see the reaction (result). You wouldn't build a factory (production app) in the lab, but it's perfect for quick experiments and learning.

💻 Code Example

codeTap to expand ⛶
1// Node.js REPL and Debugging Techniques
2
3// 1. Creating a custom REPL with pre-loaded context
4const repl = require("repl");
5const util = require("util");
6
7function startCustomRepl() {
8 const r = repl.start({
9 prompt: "myapp > ",
10 useColors: true,
11 });
12
13 // Pre-load commonly used modules
14 r.context.db = { find: (q) => `Querying: ${JSON.stringify(q)}` };
15 r.context.config = { port: 3000, env: "development" };
16 r.context._ = require("lodash"); // If installed
17
18 console.log("Custom REPL started. Pre-loaded: db, config");
19}
20
21// Uncomment to use: startCustomRepl();
22
23// 2. util.inspect — Deep object inspection
24const complexObj = {
25 name: "Server",
26 nested: {
27 deep: {
28 deeper: {
29 value: "found it!",
30 },
31 },
32 },
33 date: new Date(),
34 regex: /^hello$/gi,
35 fn: function myFunc() {},
36 sym: Symbol("id"),
37 arr: [1, [2, [3, [4]]]],
38};
39
40// Default depth is 2 — you miss deep values
41console.log("Default inspect:", util.inspect(complexObj));
42
43// Full depth with colors
44console.log("\nFull inspect:", util.inspect(complexObj, {
45 depth: null, // Show ALL levels
46 colors: true, // Syntax highlighting
47 showHidden: false, // Show non-enumerable properties
48 compact: false, // Pretty-print
49 sorted: true, // Sort object keys
50}));
51
52// 3. console.dir with depth control
53console.dir(complexObj, { depth: null, colors: true });
54
55// 4. console.table for structured data
56const users = [
57 { name: "Alice", role: "admin", age: 30 },
58 { name: "Bob", role: "user", age: 25 },
59 { name: "Charlie", role: "moderator", age: 35 },
60];
61console.table(users);
62console.table(users, ["name", "role"]); // Select columns
63
64// 5. console.trace — Stack trace for debugging
65function a() { b(); }
66function b() { c(); }
67function c() {
68 console.trace("How did we get here?");
69}
70// a(); // Uncomment to see the full call stack
71
72// 6. Error handling patterns
73// ❌ BAD: Swallowing errors
74function badErrorHandling() {
75 try {
76 JSON.parse("invalid json");
77 } catch (e) {
78 // Silent failure — impossible to debug
79 }
80}
81
82// ✅ GOOD: Handle errors meaningfully
83function goodErrorHandling(jsonString) {
84 try {
85 return JSON.parse(jsonString);
86 } catch (error) {
87 console.error("Failed to parse JSON:", {
88 input: jsonString.substring(0, 100),
89 error: error.message,
90 stack: error.stack,
91 });
92 throw new Error(`Invalid JSON input: ${error.message}`);
93 }
94}
95
96// 7. Custom error classes
97class AppError extends Error {
98 constructor(message, statusCode, isOperational = true) {
99 super(message);
100 this.name = this.constructor.name;
101 this.statusCode = statusCode;
102 this.isOperational = isOperational;
103 Error.captureStackTrace(this, this.constructor);
104 }
105}
106
107class NotFoundError extends AppError {
108 constructor(resource) {
109 super(`${resource} not found`, 404);
110 }
111}
112
113class ValidationError extends AppError {
114 constructor(field, message) {
115 super(`Validation failed: ${field} - ${message}`, 400);
116 this.field = field;
117 }
118}
119
120// Usage
121try {
122 throw new NotFoundError("User");
123} catch (err) {
124 console.log("\n--- Custom Error ---");
125 console.log("Name:", err.name); // NotFoundError
126 console.log("Message:", err.message); // User not found
127 console.log("Status:", err.statusCode); // 404
128 console.log("Is operational:", err.isOperational); // true
129}
130
131// 8. Unhandled errors — last resort handlers
132process.on("uncaughtException", (error) => {
133 console.error("UNCAUGHT EXCEPTION:", error);
134 // Log the error, then exit (don't try to recover)
135 process.exit(1);
136});
137
138process.on("unhandledRejection", (reason, promise) => {
139 console.error("UNHANDLED REJECTION:", reason);
140 // In production: log and exit
141});

🏋️ Practice Exercise

Exercises:

  1. Start the Node.js REPL and explore os, path, and crypto modules using Tab completion
  2. Create a custom REPL that pre-loads your project's database models and utility functions
  3. Use node --inspect-brk to debug a simple script in Chrome DevTools — set breakpoints and step through
  4. Write a custom error class hierarchy: AppErrorHttpErrorNotFoundError / ForbiddenError
  5. Create a debugging utility that wraps console.log with timestamps, colors, and log levels
  6. Write a script with a deliberate bug, use console.trace() and the debugger to find and fix it

⚠️ Common Mistakes

  • Using console.log(JSON.stringify(obj)) instead of util.inspect() — JSON.stringify crashes on circular references and hides Symbols, functions, and class info

  • Catching errors without doing anything (catch (e) {}) — silent failures are the #1 source of mysterious production bugs

  • Using process.on('uncaughtException') to keep the server running — after an uncaught exception, the app state is unknown; always exit and restart

  • Not using custom error classes — throwing plain Error('something failed') doesn't carry status codes, error types, or context needed for proper handling

  • Relying only on console.log debugging — learn the Node.js inspector for complex bugs; it shows call stacks, variables, and memory in real time

💼 Interview Questions

🎤 Mock Interview

Mock interview is powered by AI for REPL & Basic Debugging. Login to unlock this feature.