What is Node.js?
Node.js is a JavaScript runtime: it runs JavaScript outside the browser on Google's V8 engine, with libuv for the event loop and asynchronous I/O. It is not a framework; Express runs on it. It adds file system, network and process access, and has no DOM. It suits servers because I/O does not block. More in the Node.js runtime.
What is the REPL in Node.js?
The REPL (Read, Eval, Print, Loop) is the interactive shell you get by running
nodewith no file. Use it to try an API without creating a file.$ node > [1, 2, 3].map((n) => n * 2) [ 2, 4, 6 ] > _.length 3 > .exit_holds the last result,.editoraccepts several lines at once, and top-levelawaitworks.Is Node.js single-threaded?
Your JavaScript runs on one thread, but the process is not single-threaded. libuv keeps a thread pool (4 threads by default) for work the OS cannot do asynchronously, such as most
fscalls,dns.lookupandzlib; network sockets use the OS's own async I/O. Results come back as callbacks on the main thread. To run JavaScript in parallel, useworker_threadsorcluster.What is the difference between JavaScript in Node.js and in the browser?
The language is the same; the APIs around it differ.
Browser Node.js Global object windowglobal(both haveglobalThis)DOM yes no File system, processes no fs,child_processModules ES modules CommonJS and ES modules Both have
fetch(Node since 18, stable in 21). In Node.jssetTimeoutreturns aTimeoutobject, in the browser a number.What is the difference between blocking and non-blocking code in Node.js?
Blocking code makes the thread wait until the work is done; non-blocking code starts it and delivers the result later through a callback, promise or event.
fs.readFileSyncblocks,fs.readFiledoes not.JavaScriptconst fs = require('node:fs'); fs.writeFileSync('demo.txt', 'hello'); console.log('1. sync read:', fs.readFileSync('demo.txt', 'utf8')); fs.readFile('demo.txt', 'utf8', (err, data) => { console.log('3. async read:', data); fs.unlinkSync('demo.txt'); }); console.log('2. after the readFile call');Sync calls are fine at startup; inside a request handler they stall every other request.
What is npm, and what is the difference between npm and npx?
npm is the package manager that ships with Node.js: it installs packages into
node_modulesand records them inpackage.json.npxruns a package's command line tool, downloading it temporarily if needed, so you need no global installs. See npm basics.npm init -y # create package.json npm install express # add a dependency npm install -D vitest # add a dev dependency npm run test # run the "test" script from package.json npx eslint . # run a tool without a global installWhat is the difference between
dependencies,devDependenciesandpeerDependencies?dependenciesare needed at runtime (express).devDependenciesonly build and test (vitest) and are skipped bynpm install --omit=dev.peerDependenciesmust be provided by the app, so a plugin does not bring a second copy of React; npm 7 and later install them automatically. More in package.json.What do
^and~mean in package.json versions?Semver ranges.
~1.4.2allows patch updates (<1.5.0);^1.4.2, npm's default, allows minor and patch updates (<2.0.0). The trap: below 1.0.0,^0.4.2allows only patches (<0.5.0), because a 0.x minor release may break things.What is
package-lock.json, and when do you usenpm ciinstead ofnpm install?package-lock.jsonrecords the exact version and hash of every installed package, transitive ones included, so installs are reproducible; commit it.npm installmay update the lock file.npm ciinstalls exactly what it says, deletesnode_modulesfirst, and fails if it disagrees withpackage.json. Usenpm ciin CI and Docker builds.What is an error-first callback?
The Node.js callback convention: the first argument is an error or
null, the rest are results.util.promisifyrelies on it.JavaScriptfunction divide(a, b, callback) { setTimeout(() => { if (b === 0) return callback(new Error('division by zero')); callback(null, a / b); }, 0); } function print(err, result) { if (err) return console.log('error:', err.message); console.log('result:', result); } divide(10, 2, print); divide(1, 0, print);Two rules:
returnafter calling back with an error, and never call the callback twice. More in callbacks.What is callback hell, and how do you avoid it?
Callback hell is each async step nested inside the previous callback, so the code drifts right and every level repeats its error check. The fix is
async/await: flat code with onetry/catch, usingutil.promisifyor Node's promise APIs (fs/promises).JavaScriptconst { promisify } = require('node:util'); function getUser(id, cb) { setTimeout(() => cb(null, { id, name: 'Ada' }), 1); } function getOrders(user, cb) { setTimeout(() => cb(null, [`${user.name}-1`, `${user.name}-2`]), 1); } function getInvoice(order, cb) { setTimeout(() => cb(null, `invoice for ${order}`), 1); } // callback hell: every step nests one level deeper and repeats the error check getUser(7, (err, user) => { if (err) return console.log(err.message); getOrders(user, (err, orders) => { if (err) return console.log(err.message); getInvoice(orders[0], (err, invoice) => { if (err) return console.log(err.message); console.log('callbacks:', invoice); flat(); }); }); }); // the same steps, flat async function flat() { try { const user = await promisify(getUser)(7); const orders = await promisify(getOrders)(user); console.log('async/await:', await promisify(getInvoice)(orders[0])); } catch (err) { console.log(err.message); } }What is
EventEmitterin Node.js?EventEmitterfromnode:eventsis the publish/subscribe class behind streams, servers and sockets:onregisters a listener,emitcalls the listeners.JavaScriptconst EventEmitter = require('node:events'); class Order extends EventEmitter { pay(amount) { this.emit('paid', amount); } } const order = new Order(); order.on('paid', (amount) => console.log('send receipt for', amount)); order.once('paid', () => console.log('first payment only')); order.pay(250); order.pay(100); console.log('listeners left:', order.listenerCount('paid'));emitis synchronous: it calls every listener, in order, before it returns.onceremoves its listener after the first call.Does
emitrun its listeners right away or later?JavaScriptconst EventEmitter = require('node:events'); const bus = new EventEmitter(); const log = []; bus.on('save', () => log.push('listener')); log.push('before'); bus.emit('save'); log.push('after'); console.log(log.join(' '));emitcalls the listeners synchronously, solisteneris pushed beforeemitreturns. An emitter is not a queue. To run a listener later, defer inside it, for example withsetImmediate.What are
__dirname,__filename,processandglobal?__dirnameand__filenameare the directory and file path of the current CommonJS module.processdescribes the running process (env,argv,exit()).globalis the global object, alsoglobalThis. ES modules have no__dirname: useimport.meta.dirname(Node 20.11 and later) or derive it:import { fileURLToPath } from 'node:url'; import path from 'node:path'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename);What type is
process.env.PORTafter you assign it a number?JavaScriptprocess.env.PORT = 3000; const port = process.env.PORT; console.log(typeof port, port === 3000, port + 1);Every
process.envvalue is a string; Node converts what you assign. Soport === 3000is false andport + 1concatenates. Parse configuration once at startup:Number(process.env.PORT ?? 3000).What is
NODE_ENV, and why set it toproduction?NODE_ENVis a convention that libraries read to choose development or production behavior; Node.js itself ignores it. Withproduction, Express caches view templates and hides stack traces, andnpm installskipsdevDependencies. Read it once into a config object instead of branching on it across the codebase.How do you read command line arguments in Node.js?
process.argvholds the node binary, the script, then your arguments from index 2. For flags, use the built-inutil.parseArgs(Node 18.3 and later).// node greet.js --name Ada --loud const { parseArgs } = require('node:util'); const { values } = parseArgs({ args: process.argv.slice(2), options: { name: { type: 'string', default: 'world' }, loud: { type: 'boolean' }, }, }); const text = `hello ${values.name}`; console.log(values.loud ? text.toUpperCase() : text); // HELLO ADAWhen should you use Node.js, and when should you not?
Use Node.js for I/O bound services: APIs, chat and WebSockets, proxies, streaming, CLI tools. Waiting costs almost nothing, so one thread holds thousands of connections. Avoid it for CPU bound work such as video encoding or ML training: a long computation blocks the event loop and every request waits, unless you move it to
worker_threadsor another service.
Node.js Basics Interview Questions
What a first backend round checks: what Node.js is, non-blocking I/O, npm and the built-in globals.